One interesting use of lambda expressions is to return other lambda expressions. For example, look at this program:
(define add (lambda (x) (lambda (y) (+ x y)))
This code creates a new procedure called add, which takes one argument named x. When add is called, it binds x to its parameter and returns a new procedure that takes another variable named y. When this procedure is called, it uses both x and y to produce the result. Some uses of add:
(define add2 (add 2)) ; Creates add2 (add2 3) ; Returns 5 (add2 1000) ; Returns 1002
The procedure add2 has its own copy of the variable x, even if add is called again:
(define add99 (add 99)) ; Creates add99 (add99 3) ; Returns 102 (add2 3) ; Still returns 5 (add99 (add2 5)) ; Returns 106
Notice how add2 and add99 can be used in conjunction, each with its own definition of x. The binding environments can be pictured as in Figure 12.1 . In order to make this picture legible, environment ancestor pointers and empty binding environments have been omitted.
Figure 12.1. Binding environment with add2 and add99
The procedure named anonymous2 computes (lambda (x) (lambda (y) (+ x y))). Its own binding environment is empty, but it contains a pointer back to the default binding environment. There are two instances of the procedure anonymous3, with two different binding environments. The ancestor of each of these binding environments is the empty binding environment for anonymous2 whose ancestor in turn is the top-level binding environment.
The symbol add points to an instance of anonymous2. When you evaluate (add 2), the call method of anonymous2 is invoked. The argument is x. The body evaluates the form (lambda (y) (+ x y)). The call method of anonymous2 begins by binding its first argument to x:
; Evaluate (lambda (y) (+ x y)) .method call([Ljava/lang/Object;)Ljava/lang/Object; new Environment dup aload_0 ; Get this.env, which is getfield Environment/env LEnvironment; ; is the default ; environment invokespecial Environment/<init>(LEnvironment;)V ; The new binding environment is on the stack ; Now bind x in the new environment to the value of argument 0 dup ; Dup the binding environment ldc "x" ; Get symbol x aload_1 ; Get the value of arg iconst_0 aaload invokevirtual Environment/bind ; Do the binding (Ljava/lang/String;Ljava/lang/Object;)V astore_1 ; Store the environment ; in local 1
Next comes the body, which evaluates the form (lambda (y) (+ x y)). Assume that this lambda has been compiled into a class called anonymous3. The next thing anonymous2 has to do is create an instance of anonymous3, giving it the current binding environment as its environment. Remember that the current binding environment has a binding for x and points back to the original binding environment for anonymous2.
To create anonymous3, the code is
new anonymous3 ; Create the lambda object dup aload_1 ; With the current environment invokespecial anonymous3/<init>(LEnvironment;)V areturn
This code creates an instance of anonymous3 with the newly created binding environment. This is how add2 and add99 can both exist without interfering with one another. When add is called the first time, with value 2, an environment is created binding x to 2, and it is stored in one instance of the class anonymous3. The second time add is called, with 99, another new environment is created that binds x to 99. This new environment is stored in a different instance of anonymous3. The two objects share the same code, since they have the same class, but they have different binding environments.