;; Consider the following LISP expression: ((lambda (x) (let ((makeInc (lambda (inc) (lambda (x) (+ x inc))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x))))) 3) ;; It will evaluate differently under dynamic scoping and lexical scoping. ;; ;; Dynamic scoping comes down to looking up the value of a variable by ;; name, at the moment the variable is referenced, i.e., conceptually, ;; the moment when list expression it is contained in gets ;; evaluated---or, equivalently, when the bytecode this expressing has ;; been compiled into gets executed. ;; Use Emacs LISP in Emacs 24 to evaluate this under its default ;; dynamic scoping. You'll get an error: "inc" is unbound in ;; the global symbol table when (+ x inc) is evaluated. Stack: Debugger entered--Lisp error: (void-variable inc) (+ x inc) (lambda (x) (+ x inc))(3) funcall((lambda (x) (+ x inc)) 3) (+ (funcall inc1 x) (funcall inc5 x)) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x))) (let ((makeInc (function (lambda (inc) (function (lambda (x) (+ x inc))))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x)))) (lambda (x) (let ((makeInc (function (lambda (inc) (function (lambda ... ...)))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x)))))(3) eval(((lambda (x) (let ((makeInc (function (lambda (inc) (function ...))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x))))) 3) nil) ;; from this stack you see the order of evaluation, as ELISP ;; recursively descends into the exressions it needs to evaluate ;; before it can apply to the top-level LAMBDA to 3 and print the ;; result. You can see evaluation descending into LETs, then into ;; function calls, then into calling the LAMBDA returned by ;; makeInc---which is another list at that point (actually, that list ;; already processed by a byte-compiler). ;; ;; so, (funcall inc1 x) is evaluated in turn. inc1 in in is what makeInc ;; returns: the program (lambda (x) (+ x inc)) , in which ;; inc is to be looked-up when the program runs. Unlike lexical scoping, ;; there's no capture of "inc"---it was bound to 1 when ;; (let ((inc1 (funcall makeInc 1)) was evaluated in the LET, but ;; now this binding is gone, leaving just a bare result, (lambda (x) (+ x inc)). ;; Then this resulting program is applied to 3 for x---but look-up ;; for inc turns up no binding. Hence the error. ;; Tracing through the actual byte-code (built under the same default ;; dynamic binding) gets the same result: ;; (We can only compile/disassemble lambdas and functions, so omit the final "3") (byte-compile '(lambda (x) (let ((makeInc (lambda (inc) (lambda (x) (+ x inc))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x)))))) #[(x) "\304\211\305!\306!\211 ! !\\+\207" [makeInc inc5 inc1 x #[(inc) "\300\207" [#[... " \\\207" [x inc] 2]] 1] 1 5] 4] ;; (see http://nullprogram.com/blog/2014/01/04/ for what these things ;; are, and observe that the so-called constants vectors contain ;; _any_ values, including compiled bytecode for the bodies of ;; lambdas, names/symbols of variables, any actual integers ;; constants, etc. Note that the output here is truncated with ... ;; ;; Disassembling this: (disassemble (byte-compile '(lambda (x) (let ((makeInc (lambda (inc) (lambda (x) (+ x inc))))) (let ((inc1 (funcall makeInc 1)) (inc5 (funcall makeInc 5))) (+ (funcall inc1 x) (funcall inc5 x))))))) nil -------------- pasted from another buffer -------------- byte code: args: (x) 0 constant ; lambda evaluated in the top LET (to be bound to makeInc) args: (inc) ; it is pushed by (0) on top of the stack 0 constant args: (x) ; body of (+ inc x) 0 varref x ; look up x by name, push on top of the stack 1 varref inc ; look up inc, ditto 2 plus ; when this code is executed, inc lookup fails 3 return 1 return ; now the above has created a LAMBDA on top of the stack 1 dup ; ...and now there are two 2 varbind makeInc ; top LET's binding down, now makeInc's bound to it ; this consumed one copy off the stack, another still on top 3 constant 1 4 call 1 ; push result of (funcall makeInc 1) on top 5 varref makeInc ; push makeInc's value on top of the stack again 6 constant 5 7 call 1 ; push result of (funcall makeInc 5) 8 varbind inc5 ; and that result is consumed and bound to inc5 9 dup ; this dups the top, which is the result of (4) 10 varbind inc1 ; bind to inc1 and consume one copy 11 varref x ; look up x by name (that'll be "3", what top-level lambda is applied to) 12 call 1 ; this calls (lambda (x) (+ inc x)), with the bytecode as above---and fails 13 varref inc5 ; --not reached-- 14 varref x 15 call 1 16 plus 17 unbind 3 18 return ;; -------------- Now switch to lexical scoping -------------- (setq lexical-binding t) t ;; ;; Lexical scoping is a lot easier to think about. Under lexical ;; scoping, expressions will close over the variables referenced in ;; them at the time of their creation (rather than the time of their ;; execution). This makes scoping predictable---you can see what the ;; values will be at the time a closure is executed by looking at the ;; containing code; since these values and variables (or their copies) ;; are going to be around at the execution time, you already know them ;; at byte-compile time. No name-based lookup is involved; in fact, ;; the byte-compiled code need not contain any names at all---unlike ;; in dynamic scoping, where it needs to carry the names around with ;; the compiled byte-code, as the look-up keys for values. ((lambda (x) (let ((makeInc (lambda (inc) (lambda (x) (+ x inc))))) ; this returned lambda will close over inc in each call (let ((inc1 (funcall makeInc 1)) ; inc is closed over its value of 1 and kept so (inc5 (funcall makeInc 5))) ; inc is closed over its value of 5 and kept so (+ (funcall inc1 x) (funcall inc5 x))))) ; hence: (+ (+ 3 1) (+ 3 5)) 3) 12