The F# “ref” type
Posted by Brian on November 9, 2008
I haven’t written much about the F# "ref" type, as it’s not used all that often. But I have an interesting blog entry coming up that needs "ref", so I thought I should write a short blog entry on the topic to get the basics out of the way.
Values and Variables; "let" and "let mutable"
In F#, immutability is the default; when I write
the symbol ‘x’ is permanently bound to the value 1 for the whole scope of ‘x’.
F# introduces the more verbose "let mutable" syntax for creating mutable variables, as in
printfn "%d" x // 1
x <- x + 1
printfn "%d" x // 2
The value of a mutable variable can be accessed just by referring to the variable name (e.g. "x"), and an assignment operator "<-" mutates the variable’s value.
That information is likely "old hat" to you. But did you know there is another way to do mutability in F#?
The "ref" type
F# has another construct for mutable state that it inherits from OCaml: refs. A "ref" is a mutable heap-allocated cell that holds one piece of data. Here’s a short example:
printfn "%d" !y // 1
y := !y + 1
printfn "%d" !y // 2
In that snippet, "y" has type "int ref" (a.k.a. "Ref<int>"). There are three notable entities here:
- The function "ref", which allocates a new ref cell given an initial value,
- The prefix operator ‘!’, which dereferences a ref cell (returns the value stored inside it), and
- The ref assignment operator ":=", which updates the value stored in the ref cell.
The syntax is a little ugly in my opinion (especially when so many of us are now accustomed to ‘!’ meaning "not"), but it’s easy and straightforward to use. For those who like to see a more operational definition of the construct, here is a possible implementation of a type that’s very similar to the one built-in to the F# library:
let mutable contents = x
member this.Contents with get() = contents
and set(x) = contents <- x
let ref x =
let (:=) (x:MyRef<_>) y =
x.Contents <- y
let (!) (x:MyRef<_>) =
(In fact, both in F# and OCaml, the built-in ref type uses a "record" as an implementation, but I have said very little about records on this blog, and didn’t want to explain one unfamiliar construct via another unfamiliar one, so here I chose to write "MyRef" as a class, instead.) Straightforward, yes?
So of course this begs the question: if F# has "let mutable", when would I ever use "ref" (other than to author OCaml-compatible code)? It turns out that in some situations you must use "ref" instead of "let mutable", and I’ll describe those situations… in the next blog entry.
Here is a teaser for next time, though. What does the following C# code print? Is it 0, 2, 4, 6, 8? Perhaps you should run it and find out.
for (int i = 0; i < 5; ++i)
actions.Add( () => i * 2 );
foreach (var act in actions)
Console.WriteLine( act() );
We’ll see how this C# code snippet relates to F# "ref" next time, too!