Capturing mutables in f#
I was talking about F# with a coworker recently and we were discussing the merits of a stateless system. Both of us really like the enforcement of having to inject state, and when necessary, returning a new modified copy of state. Functional languages want you to work with this pattern, but like with all things software, it’s good to be able to break the rules. This is one of the things I like about F#, you can create mutables and do work imperatively if you need to.
But, there is a small caveat with mutables: you can’t close over them. Look at the following example:
let g() =
let mutable f = 0;
fun () -\> Console.WriteLine f
The intent is that calling g()
would give you a new function that writes f
to the console. In C# it would be the same as
public Action g(){
int f = 0;
return () =\> Console.WriteLine(f);
}
Both examples look functionally the same, but the F# example actually gives you the following compiler error:
The mutable variable ‘f’ is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via ‘ref’ and ‘!’.
But, the C# version is totally fine. Why?
The reason is because F# mutable values are always stack allocated. To close on a variable, the variable needs to be allocated on the heap (or copied by value). This is why you can close on objects that aren’t mutable (you close on their reference) and values that aren’t mutable (they are closed by value, i.e. copied). If you closed on a stack allocated type it wouldn’t work; stack objects are popped off after the function loses scope. This is the basis of stack unwinding. After the stack is unwound, the reference to the value you closed on would point to garbage!
So why does the C# version work? f
looks like a stack allocated value type to me. The nuance is that the C# compiler actually makes f
become a heap allocated value type. Here is a quote from Eric Lipperts blog explaining this (emphasis mine):
in the Microsoft implementation of C# on the desktop CLR, value types are stored on the stack when the value is a local variable or temporary that is not a closed-over local variable of a lambda or anonymous method , and the method body is not an iterator block, and the jitter chooses to not enregister the value.
So C# actually moves the value type to the heap to be declared because it needs to be accessed later via the closure. If you didn’t do that, then the value type wouldn’t exist when the closure is executed since the stack reference would have been lost (stacks are popped off when functions return).
F#, then, is much stricter about its stack vs heap allocations and opted to not do this magic for you. I think their decision aligns with the functional philosophy of statelessness; they obviously could have done the magic for you but chose not to.
Instead, if you do need to return a captured mutable value in a function closure you have to use what is called a reference cell. All a reference cell is is a heap allocated mutable variable, which is exactly what you need for returned closures to work.
A modified version of our example that would now work looks like this:
let g() =
let f = ref 0;
fun () -\> Console.WriteLine !f
g()()
Notice the !
which dereferences the cell. This example outputs
0
Without the !
, though, you’d get
Microsoft.FSharp.Core.FSharpRef`1[System.Int32]
Showing you that f
isn’t really an int, it’s a boxed heap value of an int.