(Re)generate: (find-lexical-intro) Source code: (find-efunction 'find-lexical-intro) More intros: (find-eev-quick-intro) (find-eev-intro) (find-elisp-intro) This buffer is _temporary_ and _editable_. It is meant as both a tutorial and a sandbox. This will become a _tutorial_ on lexical binding in the future, and it will be a complement to this elisp tutorial: (find-elisp-intro) but at this moment I know far less about lexical binding that I should, and this is just an embarassingly small collection of links, tests, and examples... See: (find-eevfile "eev-template0.el" "intrinsically INCOMPATIBLE with lexical")
0. How to use thisUsually people select between lexical binding and dynamic binding by putting their functions that use lexical binding in files with a "-*- lexical-binding:t -*-" in their first line, like this: (find-efile "play/tetris.el" "lexical-binding:t") See: (find-elnode "Using Lexical Binding") (find-elnode "Using Lexical Binding" "first line") Here we will do something much more low-level. `(eval CODE)' and `(eval CODE nil)' both eval CODE using dynamic binding, but `(eval CODE t)' and `(eval CODE 'lexical)' eval CODE using lexical binding. In dynamic binding all `defun's generate lambdas, but in lexical binding some `defun's generate closures. You can test this by running the four sexps below with `M-e' - the `(eval ... 'lexical)' stores a closure in the function cell of `foo': (eval '(let ((x 42)) (defun foo () (* x x))) nil) (symbol-function 'foo) (eval '(let ((x 42)) (defun foo () (* x x))) 'lexical) (symbol-function 'foo) We will also use another technique, based on `M-e', for making code eval-able in both dynamic binding mode and lexical binding mode. Let me list some facts... 1) dynamic binding is still the current default, but it is being phased out and may be declared obsolete in a few years - AND EVEN KILLED COMPLETELY, despite the complaints of the many people who love it, like me; 2) `M-e' goes to the end of the line, reads the sexp before point, and `eval's it; 3) `M-e' _sort of_ emulates `C-e C-x C-e', but `C-x C-e' doesn't run a plain `eval' - it runs a function based on `eval' that has lots of bells and whistles, and that is too complex for my tiny brain. Take a look at its code: (eek "M-h M-k C-x C-e ;; eval-last-sexp") (find-efunctiondescr 'eval-last-sexp) (find-efunction 'eval-last-sexp) 4) `C-x C-e' uses the buffer-local variable `lexical-binding' to decide whether it should use dynamic or lexical binding, while `M-e' does not. Try to eval the sexps below with a series or `C-e C-x C-e's: (setq-local lexical-binding t) (let ((x 42)) (defun foo () (* x x))) (symbol-function 'foo) (setq-local lexical-binding nil) (let ((x 42)) (defun foo () (* x x))) (symbol-function 'foo) You will see that the first block generates a closure and the second does not. If you try to execute those six sexps with `M-e's you'll get lambdas in both blocks. 5) I chose to use a simple `eval' without the second argument in the plain `M-e' because: a) it is simpler to understand, b) it is simpler to explain to beginners, and b) people can change it by redefining this function: (find-eev "eev-eval.el" "arg-variants" "ee-eval-last-sexp-default") 6) `M-e' supports numeric prefixes that select alternate actions - for example, `M-0 M-e' highlights the sexp instead of executing it - and it's easy to add new alternate actions. See: (find-eev "eev-eval.el" "ee-eval-last-sexp") (find-eev "eev-eval.el" "arg-variants") 7) `M-1 M-1 M-e' (or: `M-11e') uses `(eval ... 'lexical)' instead of the plain `eval'. See: (find-efunction 'ee-eval-last-sexp-default) (find-efunction 'ee-eval-last-sexp-11) (find-efunction 'ee-eval-lexical) Try to execute the 4-line sexp below with both `M-e' and `M-11e': (let ((x 42)) (defun foo () (* x x)) (symbol-function 'foo) ) In the rest of this tutorial I will suppose that the reader knows how to use `M-e' to eval sexps in the "old" dynamic binding mode and `M-11e' to eval sexps in new lexical binding mode, and knows how to run code blocks in BOTH dynamic and lexical binding modes to compare the results. (The rest of this tutorial is just a first draft)
1. `lambda' and `function'See: (find-elnode "Anonymous Functions" "Macro: lambda") (find-elnode "Anonymous Functions" "Special Form: function") (find-elnode "Anonymous Functions" "Special Form: function" "converted") (let ((x 42)) (lambda () x) ) (let ((x 42)) (function (lambda () x) ) )
2. How closures workSee: (find-elnode "Dynamic Binding" "defun getx") (find-elnode "Lexical Binding" "defun getx") (find-elnode "Void Variables") (let ((x 20)) (defun getx () x) ) (makunbound 'x) (setq x 99) (getx) (let ((x 42)) (getx))
3. `get/set';; These fsets work as defuns. ;; See: (find-elnode "Function Cells") (fset 'foo (lambda () 20)) (fset 'foo (lambda () 42)) (foo) ;; This is the only sexp in this block that needs ;; to be run in lexical binding mode. ;; (defun get/set0 () "Return a list with a `getter' closure and a `setter' closure. The getter and the setter share the same lexical environment - which means that they operate on the same `x'. Different calls to this function generate getters and setters with independent lexical environments - which means that they operate on independent `x's. This defun needs to be executed in lexical binding mode." (let* ((x nil)) (list (lambda () x) (lambda (newvalue) (setq x newvalue))))) (defun get/set (getter setter) "Define a SETTER and a GETTER that operate on the same variable. SETTER and GETTER are symbols: names of functions. The variable lives in a lexical environment that is shared by both the GETTER and the SETTER. Each call to this function generates a different lexical environment." (let ((gs (get/set0))) (fset getter (nth 0 gs)) (fset setter (nth 1 gs)))) ;; Check that geta/seta and getb/setb operate on different ;; variables: ;; (get/set 'geta 'seta) (get/set 'getb 'setb) (symbol-function 'geta) (symbol-function 'getb) (symbol-function 'seta) (symbol-function 'setb) (seta 20) (setb 42) (geta) (getb) ;; Check that geta/seta use the same lexical environment ;; and that getb/setb use a second lexical environment. ;; See: (find-elnode "Closures") (symbol-function 'seta) (cdr (symbol-function 'seta)) (car (cdr (symbol-function 'seta))) (eq (cadr (symbol-function 'geta)) (cadr (symbol-function 'seta)) ) (eq (cadr (symbol-function 'getb)) (cadr (symbol-function 'setb)) )
4. Alpha-conversion;; In lexical binding mode alpha-conversion works: ;; https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B1-conversion ;; Try: (setq b 3) (defun getb () b) (list (let ((a 2)) (list a (getb))) (let ((b 2)) (list b (getb))) ) ;; In lexical binding mode the 4-line `list' sexp returns a list ;; of two identical lists. These two `let' sexps are equivalent: ;; ;; (let ((a 2)) (list a (getb))) ;; (let ((b 2)) (list b (getb))) ;; ;; and the choice of `a' or `b' for the name of the variable ;; doesn't matter. In dynamic binding mode the `b' of the ;; `(let ((b ...)) ...)' is seen by the (getb), and it shadows ;; the global `b'.
5. A threadIn aug/2021 I sent an e-mail to the help-gnu-emacs mailing list asking for help to write this tutorial; its title was "Lexical vs. dynamic: small examples?". Few people sent small examples, but some of the messages in the thread were fantastically good. Here is a link to the thread itself, to two of my posts in it, and to the messages that I considered "fantastically good"... https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/threads.html#00283 https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00283.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00294.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00314.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00342.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00344.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00345.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00352.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00355.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00350.html https://lists.gnu.org/archive/html/help-gnu-emacs/2021-08/msg00341.html And here are (elisp hyper)links to them with descriptions: (defun ee-dynlex-url (nnnnn) (format "https://lists.gnu.org/archive/html/help-gnu-emacs/%s/msg%s.html" "2021-08" nnnnn)) (defun find-dynlexpost (nnnnn &rest ignored) (find-eww (ee-dynlex-url nnnnn))) (find-dynlexpost "00283" "Edrx" "initial post") (find-dynlexpost "00294" "Edrx" "primary source") (find-dynlexpost "00314" "Drew" "for the dynamic extent of the function call") (find-dynlexpost "00342" "Drew" "binds special vars dynamically and") (find-dynlexpost "00344" "Stefan" "same as in code analysis") (find-dynlexpost "00345" "Drew" "by pretty much all of the wizards of Lisp") (find-dynlexpost "00352" "Drew" "dynamically binds a lambda to a name") (find-dynlexpost "00355" "Drew" "dyna-show.el") (find-dynlexpost "00355" "Stefan" "lexical-let and dlet") (find-dynlexpost "00341" "Drew" "scope and extent are different things")