To enable it, you need to set lexical-binding to t. This is buffer local, so you need to set it for every buffer that you want to have lexical binding in.
Here's a demonstration in an ielm session (M-x ielm).
ELISP> (setq lexical-binding t)Without lexical binding, the variable x would be a dynamic variable, and so when we set it to 100 later in the session, the 1-adder would change to a 100-adder inadvertently. In fact, if we try, we can't even get to that point. Here's the same session without lexical binding:
t
ELISP> (defun make-adder (base) (lambda (num) (+ num base)))
make-adder
ELISP> (setq x 1)
1
ELISP> (setq 1-adder (make-adder x))
(closure
((base . 1)
t)
(num)
(+ num base))
ELISP> (funcall 1-adder 10)
11
ELISP> (funcall 1-adder 100)
101
ELISP> (setq x 100)
100
ELISP> (funcall 1-adder 100)
101
ELISP> lexical-bindingWhat's going on here? Notice that when we make the 1-adder without lexical binding, we don't get a closure. Instead, we just get a function that references num and base. num is passed into the function, but base is a dynamic variable, so it is interpreted in the emacs global context, as opposed to a context inside a closure. The variable base is not bound to a value in the global context, and when we execute the lambda, which refers to base, emacs looks for the base variable, cannot find it, and throws an error.
nil
ELISP> (defun make-adder (base) (lambda (num) (+ num base)))
make-adder
ELISP> (setq x 1)
1
ELISP> (setq 1-adder (make-adder x))
(lambda
(num)
(+ num base))
ELISP> (funcall 1-adder 10)
*** Eval error *** Symbol's value as variable is void: base
This doesn't happen with lexical binding. In that case, we can actually see the closure itself being returned when there is lexical binding:
(closureThe closure here shows a structure in which we can interpret to mean that base is being bound to 1. You can also see num is the argument (since it is not in the same s-expression as the binding of base), and then it proceeds with the simple addition function.
((base . 1)
t)
(num)
(+ num base))
I haven't yet made use of closures in my emacs code. I don't really need it in my initialization files, and if I writes modules that assume it then almost no one can yet use it. So I'll give it time, but it's a huge step in making elisp a better lisp. Read more about lexical bindings Paul Graham's wonderful free book On Lisp.
Lexical bindings are not only a good feature in themselves, they are also is an important step on the way to multithreading. Having nothing but global variables makes threading quite challenging, and lexical bindings in the standard elisp packages can help make multithreading easier.
No comments:
Post a Comment