0
votes

I'm trying to understand the following two snippets of code:

(defun make-adder1 (n) `(lambda (x) (+ ,n x)))

(defun make-adder2 (n) (lexical-let ((n n)) (lambda (x) (+ n x))))

These both seem to produce callables:

(funcall (make-adder1 3) 5) ;; returns 8
(funcall (make-adder2 3) 5) ;; returns 8

These both work. I have two main questions:

1) I don't understand the disparity in "quoting level" between the two approaches. In the first case, the lambda expression is quoted, which means the "symbol itself" is returned instead of the value. In the second case, it seems like the statement with the lambda will get evaluated, so the value of the lambda will be returned. Yet, these both work with funcall. When using funcall on a defun'ed function, it has to be quoted. Is lexical-let doing some kind of quoting automatically? Isn't this, kind of surprising?

2) Reading other posts on this topic, I'm given to understand that the first approach will break down under certain circumstances and deviate from what one would expect from working with lambdas and higher order functions in other languages, because elisp has dynamic scoping by default. Can someone give a concrete example of code that makes this difference apparent and explain it?

1
This must be a duplicate, but I don't have time to look for it...Drew

1 Answers

2
votes

In the first example there is no variable n in the resulting function, which is just (lambda (x) (+ 3 x)). It does not need lexical binding because there is no free variable in the lambda, i.e., no variable that needs to be kept in a binding of a closure. If you don't need the variable n to be available, as a variable in uses of the function, i.e., if its value at function definition time (=3) is all you need, then the first example is all you need.

(fset 'ad1 (make-adder1 3))

(symbol-function 'ad1)

returns:

(lambda (x) (+ 3 x))

The second example creates what is, in effect, a function that creates and applies a complicated closure.

(fset 'ad2 (make-adder2 3))

(symbol-function 'ad2)

returns

(lambda (&rest --cl-rest--)
  (apply (quote (closure ((--cl-n-- . --n--) (n . 3) t)
                         (G69710 x)
                         (+ (symbol-value G69710) x)))
         (quote --n--)
         --cl-rest--))

A third option is to use a lexical-binding file-local variable and use the most straightforward definition. This creates a simple closure.

;;; foo.el --- toto -*- lexical-binding: t -*-
(defun make-adder3 (n) (lambda (x) (+ n x)))

(fset 'ad3 (make-adder3 3))

(symbol-function 'ad3)

returns:

(closure ((n . 3) t) (x) (+ n x))

(symbol-function 'make-adder1)

returns:

(lambda (n)
  (list (quote lambda)
    (quote (x))
    (cons (quote +) (cons n (quote (x))))))


(symbol-function 'make-adder2)

returns:

(closure (t)
     (n)
     (let ((--cl-n-- (make-symbol "--n--")))
       (let* ((v --cl-n--)) (set v n))
       (list (quote lambda)
         (quote (&rest --cl-rest--))
         (list (quote apply)
           (list (quote quote)
             (function
              (lambda (G69709 x)
                (+ (symbol-value G69709) x))))
           (list (quote quote) --cl-n--)
           (quote --cl-rest--)))))


(symbol-function 'make-adder3)

returns

(closure (t) (n) (function (lambda (x) (+ n x))))