reddit / emacs

some examples on closures and lexical-binding

tonyaldon · 2022-02-11 15:02
EDIT: (all the posts in Org format) [https://github.com/tonyaldon/posts](https://github.com/tonyaldon/posts) Hi Emacsers, I've spent some time learning Elisp closures and lexical binding. Although the documentation is clear with simple examples, I needed to to play with other examples to get my bearings. As you might be interested, I share them with you :) The first section contains the info nodes and help corresponding to the examples. The second section shows 10 "basic" examples using `lexical-binding`, `lambda`, `defun`, `let`, `lexical-let`, `setq` and `defvar`. And the third section shows a more advanced example using `lambda`, `let`, `dolist` and `defun`. Have a nice day, ps: In the documentation, we can read "Lexical binding is also more compatible with concurrency, which was added to Emacs in version 26.1.". Is someone know why is this? And can you indicate me the files in the emacs source code where I can see this benefits? I'm really curious... thank you. # info and help Info nodes: * M-x eval-expression RET (info "(elisp)Variable Scoping") * M-x eval-expression RET (info "(elisp)Closures") help: * C-h v lexical-binding * C-h f defvar * C-h f lambda * C-h f lexical-let # 10 examples with lexical-binding, lambda, defun, let, lexical-let, setq, defvar example 1 (setq lexical-binding nil) (lambda (x) (* x x)) ; (lambda (x) (* x x)) example 2 (setq lexical-binding t) (lambda (x) (* x x)) ; (closure (t) (x) (* x x)) example 3 (setq lexical-binding nil) (let ((x 1)) (lambda (y) (+ x y))) ;; (lambda (y) (+ x y)) example 4 (setq lexical-binding t) (let ((x 1)) (lambda (y) (+ x y))) ;; (closure ((x . 1) t) (y) (+ x y)) example 5 (setq lexical-binding t) (let ((n 1)) (defun n+ (y) (+ n y))) (n+ 1) ; 2 (let ((n -10)) (n+ 1)) ; 2 example 6 (setq lexical-binding nil) (let ((n 1)) (defun n+ (y) (+ n y))) (should-error (n+ 1) :type 'void-variable) ; (void-variable n) (let ((n -10)) (n+ 1)) ; -9 example 7 (setq lexical-binding nil) (lexical-let ((n 1)) (defun n+ (y) (+ n y))) (n+ 1) ; 2 (let ((n -10)) (n+ 1)) ; 2 example 8 (setq lexical-binding t) (setq xx 10) (let ((xx 1)) (defun xx+ (y) (+ xx y))) (xx+ 1) ; 2 (let ((x -10)) (xx+ 1)) ; 2 example 9 (setq lexical-binding t) (defvar xxx 10) (let ((xxx 1)) (defun xxx+ (y) (+ xxx y))) (xxx+ 1) ; 11 (let ((xxx -10)) (xxx+ 1)) ; -9 example 10 (setq lexical-binding t) (let ((xxxx 1)) (defun xxxx+ (y) (+ y xxxx))) (defvar xxxx 10) (xxxx+ 1) ; 11 (let ((xxxx -10)) (xxxx+ 1)) ; -9 # advanced example using lambda, let, dolist, defun Evaluating the following forms with lexical binding: (setq lexical-binding t) (defun call-f (f x) `(:x-in-call-f ,x :result-of-f ,(funcall f x) :type-of-f ,(car f) :env-of-f ,(and (eq (car f) 'closure) (cadr f)))) (dolist (x '(1 2 3)) (let ((f (lambda (y) `(:x-in-f ,x :y ,y)))) (message "%S" (append `(:x-in-dolist ,x) (call-f f -1))))) prints out the following into the message buffer: (:x-in-dolist 1 :x-in-call-f -1 :result-of-f (:x-in-f 1 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 1 2 3) t)) (:x-in-dolist 2 :x-in-call-f -1 :result-of-f (:x-in-f 2 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 2 3) t)) (:x-in-dolist 3 :x-in-call-f -1 :result-of-f (:x-in-f 3 :y -1) :type-of-f closure :env-of-f ((--dolist-tail-- 3) t)) Evaluating the following forms with dynamic binding: (setq lexical-binding nil) (defun call-f (f x) `(:x-in-call-f ,x :result-of-f ,(funcall f x) :type-of-f ,(car f) :env-of-f ,(and (eq (car f) 'closure) (cadr f)))) (dolist (x '(1 2 3)) (let ((f (lambda (y) `(:x-in-f ,x :y ,y)))) (message "%S" (append `(:x-in-dolist ,x) (call-f f -1))))) prints out the following into the message buffer: (:x-in-dolist 1 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil) (:x-in-dolist 2 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil) (:x-in-dolist 3 :x-in-call-f -1 :result-of-f (:x-in-f -1 :y -1) :type-of-f lambda :env-of-f nil)
nv-elisp · 2022-02-11 15:46
Using `setq` to set `lexical-binding` is not recommended in actual code.
tonyaldon · 2022-02-11 16:00
Can you give me more information? Or, a part of the source code that I can read to better understand this concept?
patrick_thomson · 2022-02-11 18:13
`lexical-binding` is unlike other variables in that it must be set as the first line of a file. Furthermore, if you put it in an init file you can end up with `lexical-binding` applied to the file in which the init is evaluated (usually the scratch buffer). It just generally doesn’t do what you want. Either set it as a file-local variable with the comment syntax, or use `eval` with an explicit `LEXICAL` argument.
tonyaldon · 2022-02-11 18:38
Thank you u/patcrick_thomson. Note that, u/nv-elisp has changed his comment. it was: >Using setq to set lexical-binding does not have the effect you think it does. And my questions referred to this comment (nothing to do with the new one). Note that I've never recommended to set `lexical-binding` with `setq` in "actual code". I do it this way in the examples to emphasize that sometime we use `lexical-binding` and sometime we don't. In the examples, if you eval in order of apparition the s-exp with `eval-last-sexp` you get the commented results which I hope illustrates closures and lexical binding.
nv-elisp · 2022-02-11 18:42
> Note that, u/nv-elisp has changed his comment. Apologies. The original wording didn't convey what I was getting at and came off presumptuous. Outside of pedagogical examples, I wouldn't recommend it for the reasons mentioned above.
ilemming · 2022-04-02 02:02
Sorry for such a late comment. *I was sorting through my auto-captured notes, and it looks like I missed it when it was posted*. I just wanted to say that `lexical-let` has been deprecated since the introduction of true lexical binding in Emacs 24.1.
tonyaldon · 2022-04-05 05:13
I wasn't aware of this fact when I published this post. But then, when I added the link to its definition in the Org format of this post, I saw that `lexical-let` was defined in the file `cl.el` in the directory `obsolete` :) Anyway, thanks to you u/ilemming for letting me know. Have a nice day