views:

2067

answers:

6

I asked a question about Currying and closures where mentioned. What is a closure? How does it relate to currying?

Additional: Kyle's answer is great but to my poor procedural/OO mind Ben Childs answer is really useful.

+16  A: 

I'll give an example (in Scheme):

(define (make-counter)
  (let ((count 0))
    (lambda ()
      (set! count (+ count 1))
      count)))

(define x (make-counter))

(x) returns 1

(x) returns 2

...etc...

What this function, make-counter, does is it returns a function, which we've called x, that will count up by one each time its called. Since we're not providing any parameters to x it must somehow remember the count. It knows where to find it based on what's called lexical scoping - it must look to the spot where it's defined to find the value. This "hidden" value is what is called a closure.

Here is my currying example again:

(define (add a)
  (lambda (b)
    (+ a b)))

(define add3 (add 3))

(add3 4) returns 7

What you can see is that when you call add with the parameter a (which is 3), that value is contained in the closure of the returned function that we're defining to be add3. That way, when we call add3 it knows where to find the a value to perform the addition.

Kyle Cronin
+5  A: 

A closure is a function that can reference state in another function. For example, in Python, this uses the closure "inner":

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
John Millikin
A: 

In a normal situation, variables are bound by scoping rule: Local variables work only within the defined function. Closure is a way of breaking this rule temporarily for convenience.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

in the above code, lambda(|n| a_thing * n} is the closure because a_thing is referred by the lambda (an anonymous function creator).

Now, if you put the resulting anonymous function in a function variable.

foo = n_times(4)

foo will break the normal scoping rule and start using 4 internally.

foo.call(3)

returns 12.

eed3si9n
+13  A: 

Kyle's answer is pretty good. I think the only additional clarification is that the closure is basically a snapshot of the stack at the point that the lambda function is created. Then when the function is re-executed the stack is restored to that state before executing the function. Thus as Kyle mentions, that hidden value (count) is available when the lambda function executes.

Ben Childs
+8  A: 

To help facilitate understanding of closures it might be useful to examine how they might be implemented in a procedural language. This explanation will follow a simplistic implementation of closures in Scheme.

To start, I must introduce the concept of a namespace. When you enter a command into a Scheme interpreter, it must evaluate the various symbols in the expression and obtain their value. Example:

(define x 3)

(define y 4)

(+ x y) returns 7

The define expressions store the value 3 in the spot for x and the value 4 in the spot for y. Then when we call (+ x y), the interpreter looks up the values in the namespace and is able to perform the operation and return 7.

However, in Scheme there are expressions that allow you to temporarily override the value of a symbol. Here's an example:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

What the let keyword does is introduces a new namespace with x as the value 5. You will notice that it's still able to see that y is 4, making the sum returned to be 9. You can also see that once the expression has ended x is back to being 3. In this sense, x has been temporarily masked by the local value.

Procedural and object-oriented languages have a similar concept. Whenever you declare a variable in a function that has the same name as a global variable you get the same effect.

How would we implement this? A simple way is with a linked list - the head contains the new value and the tail contains the old namespace. When you need to look up a symbol, you start at the head and work your way down the tail.

Now let's skip to the implementation of first-class functions for the moment. More or less, a function is a set of instructions to execute when the function is called culminating in the return value. When we read in a function, we can store these instructions behind the scenes and run them when the function is called.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

We define x to be 3 and plus-x to be its parameter, y, plus the value of x. Finally we call plus-x in an environment where x has been masked by a new x, this one valued 5. If we merely store the operation, (+ x y), for the function plus-x, since we're in the context of x being 5 the result returned would be 9. This is what's called dynamic scoping.

However, Scheme, Common Lisp, and many other languages have what's called lexical scoping - in addition to storing the operation (+ x y) we also store the namespace at that particular point. That way, when we're looking up the values we can see that x, in this context, is really 3. This is a closure.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

In summary, we can use a linked list to store the state of the namespace at the time of function definition, allowing us to access variables from enclosing scopes, as well as providing us the ability to locally mask a variable without affecting the rest of the program.

Kyle Cronin
@Kyle Cronin: okay, thanks to your answer, I think that I finally have some idea what closure is about. But there is one big question: "we can use a linked list to store the state of the namespace at the time of function definition, allowing us to access variables that otherwise would no longer be in scope." `Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?`
Lazer
@Laser: Sorry, that sentence didn't make much sense, so I updated it. I hope it makes more sense now. Also, don't think of the linked list as an implementation detail (as it's very inefficient) but as a simple way of conceptualize how it could be done.
Kyle Cronin