Jul 8, 2011

f# essential - part 9 - no need to use ref record type in class

As I discussed it in post f# essential - part 5 - mutable, ref, ref record is used for closure. And mutable is similar concept of variable in c#. So that you can not use mutable defined outside of a function. So the following it is wrong

let build_t() = 
    let mutable x = 0
    let t() =
        x <- 1 + x
        printfn "you call me %i time(s)" x
    t

let t =  build_t()
t()

You have to convert it ref record type like the following.

let build_t() = 
    let x = ref 0
    let t() =
        x := 1 + !x
        printfn "you call me %i time(s)" !x

    t

If we define method in a class using F#, and we want to update the member field in a method, should we also use mutable or ref record. It turns out, both of them are supported.

type Foo() = 

    let mutable callT1Count = 0
    let callT2Count = ref 0

    member x.t1() = 
        callT1Count <- callT1Count + 1
        printfn "you call t1 %i time(s)" callT1Count

    member x.t2() = 
        callT2Count := !callT2Count + 1
        printfn "you call t2 %i time(s)" !callT2Count

let f = new Foo()

f.t1()
f.t2()

I used reflector to decompile it to c# code as follow to know that that is implemented in .net

[Serializable, CompilationMapping(SourceConstructFlags.ObjectType)]
public class Foo
{
    // Fields
    internal int callT1Count = 0;
    internal FSharpRef<int> callT2Count = Operators.Ref<int>(0);

    // Methods
    public void t1()
    {
        this.callT1Count++;
        FSharpFunc<int, Unit> func = ExtraTopLevelOperators.PrintFormatLine<FSharpFunc<int, Unit>>(new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("you call t1 %i time(s)"));
        int num = this.callT1Count;
        func.Invoke(num);
    }

    public void t2()
    {
        Operators.op_ColonEquals<int>(this.callT2Count, Operators.op_Dereference<int>(this.callT2Count) + 1);
        FSharpFunc<int, Unit> func = ExtraTopLevelOperators.PrintFormatLine<FSharpFunc<int, Unit>>(new PrintfFormat<FSharpFunc<int, Unit>, TextWriter, Unit, Unit, int>("you call t2 %i time(s)"));
        int num = Operators.op_Dereference<int>(this.callT2Count);
        func.Invoke(num);
    }
}

Why mutable is not supported in closure? I think the reason is that when you create an instance of Class, the variable (mutable) is already closed in an instance. Of course, ref type is still supported, but but I think it is double closed. We only need to use variable(mutable) in class.