Reputation: 31206
So here is my question. I have a python project that is very functionally oriented...but which fundamentally runs with classes...so F# seemed the ideal language for a compiled version of my code (I'd like to black box my source code...the python code is just sitting there in files. Not cool).
In my python class, I have a generic container--a python list. This container will contain arrays of a uniform length in a given implementation...
Here is my problem: I need to add arrays to the list after initialization.
What is the proper way to add arrays to the list in the class?
Here is what I have...but it seems like it will be slow, since it throws out the old list with a new copy (right??):
type myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value::_training
However, this fails (even after the edits suggested below) because the compiler claims that the set
function is an obj with set
rather than an obj list with set
, even though it acknowledges that _training
is a mutable obj list on the left side of the <-
in the set function...I'm stumped)
What is the proper "F#" way to do this?
Upvotes: 2
Views: 361
Reputation: 2764
The problem is that a property has a type, and the getter and setter must be compatible with that type.
If I run your code interactively, the compiler helpfully tells me exactly that!
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(value) = _training <- value::_training
// error FS3172: A property's getter and setter must have the same type.
// Property 'training' has getter of type 'obj list'
// but setter of type 'obj'.
The answer is simply to have a different method that prepends an element to the list:
type myclass() =
let mutable _training = []
member this.training
with get() = _training
and set(newTraining) = _training <- newTraining
member this.prepend(element) =
_training <- (element::_training)
member this.prependList(list) =
_training <- (list @ _training)
// test
let myList = myclass()
myList.prepend(1)
myList.prepend(2)
myList.training // output => [2; 1]
myList.prependList([3;4])
myList.training // output => [3; 4; 2; 1]
It's not a nice implementation from a functional point of view, but it does answer your question.
Note that if you do want a pure functional implementation, you don't need a class at all.
// test
let myList = []
let myList2 = 1::myList
let myList3 = 2::myList2
// myList3 => [2; 1]
let myList4 = [3;4] @ myList3
// myList4 => [3; 4; 2; 1]
Now, how to work with immutable state in a nice way -- that's a whole other question! :)
Upvotes: 2
Reputation: 233150
The 'proper' F# way to do it is to not have that myclass
class at all. A class with a single getter and setter provides no value over an immutable list.
That said, the above code doesn't compile for a number of reasons:
type
is defined with =
, not :
._training
isn't bound by with a let
expression._training
value, you must declare it let mutable
get
is obj list
, but the input value of set
(which is the symbol value
) is obj
, because of the use of the cons operator (::
). As the compiler says, the type of get
and set
must be the same.You can make it compile by addressing all these concerns:
type Myclass() =
let mutable _training = []
member this.training with get() = _training
and set(value) = _training <- value
but it would accomplish nothing. If you need a list, then pass a list. It's not that we don't believe in encapsulation in F#, but properties aren't equivalent to encapsulation anyway...
BTW, it'd be idiomatic to name types using Pascal Case, so MyClass
instead of myclass
. The leading underscore isn't used much either, so it should be training
instead of _training
.
Upvotes: 3