Chris
Chris

Reputation: 31206

F# cons operator and list in class

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

Answers (2)

Grundoon
Grundoon

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

Mark Seemann
Mark Seemann

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:

  1. A type is defined with =, not :.
  2. _training isn't bound by with a let expression.
  3. If you want to mutate the _training value, you must declare it let mutable
  4. Even so, the return value of 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

Related Questions