When I created a class containing a generic, mutable .NET Stack in F# like the example below, that stack ignores anything I push onto 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?!
Yet 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 like I'd expect.
What on earth is going on here? My understanding of immutability from previous languages (C# mostly) would say that even though the stack in the first example is an immutable member, that immutablity should only go as far the reference (aka I shouldn't be able to reassign Stack itself). I should still be able to push values to/from it. What am I missing, and if trying to mutate that stack is the wrong thing, why doesn't it throw an exception or a compile error?
If you try to compile the first version, and then use eg Reflector to decompile it to C#, you'll see that the stack member 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.
The second version cross-compiles 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 seems more like what you'd want a property to do.
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 externally, omit the next to last line.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.