views:

1327

answers:

8
+7  Q: 

F# Closure

Anyone have a decent example, preferably practical/useful, they could post demonstrating the concept?

+6  A: 

Closures can be used for any number of reasons, one of which is to reduce the sope of helper functions or values. So rather than polluting the module/namespace with random crap, you can scope them to just where they are necessary.

open System

let isStrongPassword password =

    let isLongEnough (str : string)   = str.Length > 10
    let containsNumber str =
        str |> Seq.tryfind (fun c -> Char.IsDigit(c)) |> Option.is_some
    let containsUpper str =
        str |> Seq.tryfind (fun c -> Char.IsUpper(c)) |> Option.is_some


    if isLongEnough password &&
       containsNumber password &&
       containsUpper password then
        true
    else
        false

Edit: Here's another example that captures values and brings them into an inner scope without being passed as parameters.

#light

open System

let isPasswordStrongerThan myPassword yourPassword =

    let mineIsLongerThan    (x : string) = 
        (myPassword.Length > x.Length)

    let mineHasMoreNumsThan (x : string) =
        let numDigits (x : string) = 
            x 
            |> Seq.map (Char.IsDigit) 
            |> Seq.fold (+) 0
        (numDigits myPassword > numDigits x)

    if mineIsLongerThan yourPassword && mineHasMoreNumsThan yourPassword then
        true
    else
        false

In the example 'myPassword' is used in all the inner functions, but it wasn't passed as a parameter.

Chris Smith
+2  A: 

Chris Smith's first example offers a helpful glimpse of identifier scope in F#. However, it doesn't take advantage of the bound variables available to the nested functions. In the following variation, minLength is available to the inner function because it is a closure.

open System.Text.RegularExpressions

let isStrongPassword minLength =
    fun (password : string) ->
        password.Length >= minLength &&
        Regex.IsMatch(password, "\\d") &&
        Regex.IsMatch(password, "[A-Z]")

This is a trivial use of a closure because you could accomplish the same thing by currying in F#.

ESV
A: 

ESV - as I understand it (which is also limited by an OO perspective) the closure has to do with limiting your functions (like isLongEnough) to within the scope of the method that uses them. This effectively closes (hence the term closure) these functions off from the rest of you application's code.

I think I am understanding it right, if not hopefully I'll get set straight as well ;)

AlexCuse
+3  A: 

@Alex -- that's just scope of the functions. closures build on that though.

@ESV --

the (fun c... form a closure --even though it's being applied directly and not passed around. I wouldn't say it's a great example. Something, simpler and straight forward:

let derivative dx =
    (fun f x -> (f (x+dx)) - (f x) / dx)

We are returning an environment(dx) and the anonymous function. Now, you don't necessarily have to return a function, like Chris's example. But it always has to do with using more widely scoped (or bound) variables -- the environment -- within a local context.

let filter lst f_critera =
    let rec loop_ = function
        | hd :: tl ->
            if f_critera hd then hd :: (loop_ tl)
            else (loop_tl)
        | [] -> []
    in
    loop_ lst

So, this is a closure, though I forced it a bit, but for the sake of defining them it's a decent framework. f_criteria is bound, within loop_ -- it's the environment variable.

nlucaroni
A: 

@ESV, the closures are the three let definitions that are indented (ie. isLongEnough, containsNumber and containsUpper). They are closures because they are functions defined within the scope of the isStrongPassword function.

Ted Percival
+1  A: 

In the text Functional Programming there is this definition:

Closures are functions which carry around some of the "environment" in which they were defined. In particular, a closure can reference variables which were available at the point of its definition.

This definition is probably not complete but is easy to comprehend for someone from imperative/objective language.

Robert Vuković
+1  A: 

Closures are useful for caching and memoization. For example:

let isWord (words: string list) =
  let wordTable = Set.Create(words)
  fun w -> wordTable.Contains(w)

> let isCapital = isWord ["London";"Paris";"Warsaw";"Tokyo"];;
val isCapital : (string -> bool)
> isCapital "Paris";;
val it : bool = true
> isCapital "Manchester";;
val it : bool = false

Notice how the wordTable is computed only once, when the closure is created.

More generally, closures are a useful to keep some private state. Hence, you can even recreate cons cells purely with functions.

let cons a b = function
    | true -> a
    | false -> b

let car p = p true

let cdr p = p false

> (car (cons 1 2))
val it : int = 1
> (cdr (cons 1 2))
val it : int = 2

Well, here the cons cells are immutable. But you could imagine having mutable state too. Let's make a little counter:

let makeCounter() =
    let x = ref 0
    let tick() =
        x := !x + 1
        !x
    tick

let c1 = makeCounter()
let c2 = makeCounter()

> c1()
val it : int = 1
> c1()
val it : int = 2
> c2()
val it : int = 1

It is known that closures are a poor man's objects, because objects are a poor man's closures :) You can simulate one with the other.

namin
A: 

I've struggled with this too - seeing the word "closure" thrown around by the F# experts. It sounds like "scope" (and nested scope) that are familiar, but it actually has little to do with that, and is only relevant in code that is passing around functions as values outside there original scope.

@Robert has a good quote...

Closures are functions which carry around some of the "environment" in which they were defined. In particular, a closure can reference variables which were available at the point of its definition.

The best example I've seen...

let Counter =
    let count = ref 0
    // *Return a function that carries the current context* (ie. "count", on 
    // the heap). Each time it is called, it will reference, and increment, 
    // the same location
    (fun () -> incr count; !count)

> Counter()
val it : int = 1
> Counter()
val it : int = 2 <-- !! The anonymous function has retained the context

This only works because count is a "ref" variable (ie. on the heap), and because Counter returns a function (rather than an integer)

Next is the wrong way - using the stack instead of the heap...

let Counter2 =
    let mutable count = 0
    // Attempt to reference mutable variable, from within a closure, is invalid
    (fun () -> count <- count+1; count)

This produces a very helpful error message, which tells us a lot about closures..

The mutable variable 'count' is used in an invalid way. Mutable variables may not be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'

(kudos to the author of that message!)

Syme, et al, "Expert F#" says, on usage of closures...

This is a powerful technique for hiding and encapsulating mutable state without resorting to writing new type and class definitions. It is good programming practice in polished code to ensure that all related items of mutable state are collected under some named data structure or other entity such as a function.

Javaman59