When does a mutable state value freed from heap?

On F# WikiBook under Encapsulating Mutable State section, there is a following code snippet.

> let incr =
    let counter = ref 0
    fun () ->
        counter := !counter + 1

val incr : (unit -> int)

> incr();;
val it : int = 1

> incr();;
val it : int = 2

> incr();;
val it : int = 3

At first, it seemed easy enough to swallow the fact that, mutable counter value increments everytime incr is invoked.

But after thinking about it for awhile, what I couldn’t understand were when counter is freed from heap and also how counter still refers to previous value before being incremented. How is counter that lives within incr function scope survive through multiple function calls?

So main questions are:

  • When does counter freed from heap?
  • Isn’t counter a memory leak?

The distinction between ‘lexical scope’ (where a name has meaning in the text of a program) and ‘lifetime’ (runtime duration between when object is created and destroyed) can sometimes be confusing, since often these two are highly correlated. However the technique demonstrated by this example is common in functional languages: you give an implementation detail a small lexical scope (that hides the implementation details from callers), but extend its lifetime by capturing it in a closure (so that its lifetime becomes the lifetime of the enclosing object – in this instance the ‘incr’ function). This is a common way to do encapsulation in functional programming (contrasted with the usual encapsulation technique of public/private in classes in object-oriented programming).

Now, in this particular example, it looks like ‘incr’ is a top-level function, which means its value lasts for the lifetime of the program (or interactive session if typing into fsi.exe). You could call this a ‘leak’, but it depends on intent. If you have some unique id counter you need for the entire lifetime of your entire program, then you are going to have to store that counter varable somewhere that it lasts for the whole program. So either this is ‘a leak’ or ‘a by design feature’ depending on how ‘incr’ will be used (will you need to use that function for the whole rest of the program?). In any case, the key point here is that ‘incr’ holds memory resources, so if you won’t need those resources forever, you should arrange for the closure referenced by ‘incr’ to become unreachable when it is no longer needed. Commonly this might be by making it local to some other function, e.g.

let MyComplicatedFuncThatNeedsALocalCounter args =
    let incr = 
        // as before
    // other code that uses incr
    // return some result that does not capture incr

In this case, incr is a top-level function (implemented as a static field if I’m not mistaken.) It holds a closure which in turn has a reference to that ref cell named counter. As long as this closure exists, the ref cell is kept in memory.

Now this top level binding will indeed never get garbage collected as it is a static readonly field. (in C# terms). If you, however, have closures like that with a limited lifetime (bound locally or in an object), the ref cell will be freed when the closure is garbage collected.

counter is freed from the heap when incr is no longer reachable. It’s not a memory leak because of garbage collection.

Leave a Comment