What is the difference between val member and this member in F #?
When I created a class that contains a generic, mutable .NET Stack in F # like the example below, that stack ignores whatever I click on it.
open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member this.stack: Stack<int> = new Stack<int>()
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[]" WAT?!
However, if I make the stack mutable via a property:
open System.Collections.Generic
type Interp(code: int array) =
member val PC = 0 with get, set
member val stack: Stack<int> = new Stack<int>() with get, set
member this.Code = code
let interp = Interp([|1;2;3|])
interp.stack.Push(1)
printfn "%A" interp.stack // prints "seq[1]"
Everything magically works as I expected.
What's going on here? My understanding of the immutability of previous languages ββ(mainly C #) might say that even if the stack in the first example is an immutable member, that immutability must only go by reference (otherwise I won't be able to reassign the stack itself). I would still have to click values ββon it. What am I missing, and if trying to mutate this stack is wrong, why isn't it throwing an exception or compilation error?
source to share
If you try to compile the first version and then use for example Reflector to decompile it to C #, you will see that the stack element is defined like this:
public class Interp
{
public Stack<int> stack
{
get { return new Stack<int>(); }
}
// Other members omitted for clarity...
}
As you can see, this is also valid C # code, but obviously not what you want.
Second version cross-compile to something like this:
public class Interp
{
internal int[] code;
internal Stack<int> stack@;
public Interp(int[] code) : this()
{
this.code = code;
this.stack@ = new Stack<int>();
}
public Stack<int> stack
{
get { return this.stack@; }
set { this.stack@ = value; }
}
// Other members omitted for clarity...
}
This is more like what you need to make a property.
source to share
A more idiomatic way to do what you want is this:
open System.Collections.Generic
type Interp(code: int array) =
let stack = Stack<int>()
member val PC = 0 with get, set
member this.Stack = stack
member this.Code = code
If you don't need to expose the stack from the outside, omit the next line.
source to share