Anyone have a decent example, preferably practical/useful, they could post demonstrating the concept?
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'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 - 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 ;)
@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.
@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.
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.
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.
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.