views:

113

answers:

3

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

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

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?
+1  A: 

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

Dave
Yes, but that doesn't help. Even in a GC environment memory leaks can occur. Namely when you accidentally hold on to objects that are no longer used. (Delegates to instance methods hold on to the `this` object for instance).
SealedSun
@Dave I was running the sample from FSI. Does that mean once will not go out of scope until I exit FSI?
Sung Meister
Yes, in FSI this will hang around for the lifetime of the FSI session. If you want to be able to 'let go of this', then make 'incr' mutable, or a ref, or something else that you can 'null out' later. In general, FSI sessions tend to leak a lot, as top-level names are never unbound (even when they become unreachable due to shadowing).
Brian
+4  A: 

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
Brian
+2  A: 

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.

SealedSun