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?

+3


source to share


2 answers


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.

+5


source


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.

+2


source







All Articles