Elisp Posts

Some examples on closures and lexical-binding

2022-02-11
/Tony Aldon/
comment on reddit
/
emacs revision: b8b2dd17c57b

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 will 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 see that "Lexical binding is also more compatible with concurrency, which was added to Emacs in version 26.1.".

Does anyone know why this is? If so, can you indicate to me the files in the emacs source code where I can see these 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)
    ;; Note that `lexical-let' is defined in `lisp/obsolete/cl.el'
    (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)