wiki:Tutorial3

Closures

There is an instance of very familiar idiom - apply a given procedure to each element of a given list, returning the results as a new list, without modifying original one:

(define square (lambda (x)
		 (* x x)))

(map square '(1 3 10))

; Values (1 9 100)

Lets imagine that we need to add some number, say 100, to each element of a given list. So, we need to define a procedure which adds 100 to a given number, and then map in to entire list.

(define add-100 (lambda (x)
                   (+ 100)))

(map add-100 '(1 3 10))

Values: (101 103 110)

But what if we need to add sometimes 10, other times 100 or 1000 or 10000. We need some generalized procedure, which can do it.

So, we need to somehow modify our original procedure

(define add-n (lambda (x n)
                 (+ x n)))

(map what...

Seems like it doesn't fit. We want to say add 100 to x, but there is no place to put that 100..

So, we must create a specialized procedure, that accepts 100 as its argument, and returns a procedure which adds 100 to a given number:

(define add-n (lambda (n)
		(lambda (x)
		  (+ n x))))

(map (add-n 100)
     '(1 3 10))

; Values: (101 103 110)

So, lets read it aloud: associate symbol add-n to a procedure with one argument n which returns a new procedure of one argument x, which adds n to x. Huh.

What is n? It is a symbol we used to refer to that constant 100 (or whatever it is) which we must add.

What is x? It is just another symbol we are using as a name (reference) to a number to which we must add n.

Now the most important question: How does it work? Well, when we call a procedure add-n with an argument 100, a new binding was created - a symbol n being associated with a value 100. Where? In procedure's own tiny piece of the of environment (it is called frame).

What is that frame of environment to a procedure? It is a context, which gives meaning to its symbols. (Don't you see?!)

This procedure with its own context (set of bindings) is called Closure. Learn to love them.

Now each time we need a new procedure which adds a constant to a number we could say:

(define add-1000 (add-n 1000)

Lets read it: create a new binding in the current (global) environment between symbol add-1000 and this closure (which is a procedure linked together with its own frame of environment) returned by generic procedure add-n.

Now even more interesting question. Should we always have to name everything? Why not just write down the meaning itself? Like that:

(map (lambda (x) (* x x))
     '(1 3 10))

Well, here is a trick: We must call the outer procedure, to get that new procedure which must be applied by map to each element of a given list. It means, we must call it explicitly with an actual argument right here, in-place.

(map ((lambda (n)
	(lambda (x)
	  (+ n x))) 1000)
     '(1 3 10))

; Values: (1001 1003 1010)

How, why we have done all this? To realize that this process of associating symbols with values in a context is how our natural languages works.

There is no difference between nouns (event words) and verbs (process words) because in Scheme everything is an expression - a list of symbols - common intermediate representation.

It does not matter to what kind of values some symbol is bound. Symbols are just names - references to the meaning attached to it in a given context. Nothing is fixed.

Last modified 7 years ago Last modified on Dec 5, 2012, 2:31:09 PM
Note: See TracWiki for help on using the wiki.