3

I understand how this code works:

(defvar *nums* '(2 3 5))

(defun print-nums ()
  (format t "~a~%" *nums*))

(print-nums)
-> (2 3 5)
-> NIL

I even understand how the new value of the dynamically bound variable *nums* is passed on to print-nums in this code:

(let ((*nums* '(1 2 3)))
  (print-nums))
-> (1 2 3)
-> NIL

But why doesn't the code below work the same way?

(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
                      #'(lambda () (format t "~a~%" *nums*))))

(funcall *my-nums-f*)
-> (2 3 5)
-> NIL

Does the concept of a closure not apply to dynamically bound variables, or am I doing something wrong? If I have wrongly understood the concept of a closure, could someone please explain it to me?

shardulc
  • 311
  • 1
  • 13
  • 2
    You can look at [this question](http://stackoverflow.com/questions/463463/dynamic-and-lexical-variables-in-common-lisp) (Dynamic and Lexical variables in Common Lisp) and the relative answers, in particular to [the answer of Rainer Joswig](http://stackoverflow.com/a/2386051/2382734). – Renzo Jun 25 '16 at 13:03

2 Answers2

5
(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
                      #'(lambda () (format t "~a~%" *nums*))))

Here we set *my-nums-f* to the result of

(let ((*nums* '(1 2 3)))
  #'(lambda () (format t "~a~%" *nums*)))

This form starts by dynamically binding *nums* to '(1 2 3), then returning #'(lambda () (format t "~a~%" *nums*)), which is a function. At the end, we reset *nums* back to '(2 3 5).

(funcall *my-nums-f*)

Here we call the function stored in *my-nums-f*, which is (lambda () (format t "~a~%" *nums*)). This function takes the current value of *nums*, which is '(2 3 5).

-> (2 3 5)
-> NIL

You seem to expect that the lambda somehow inlines the current values of all variables used in the function body, which would indeed result in (format t "~a~%" '(1 2 3)) at that point.

But that's not how it works: The function refers to the variable itself, not a shapshot of the variable's value. That's why the function sees whatever the current value of *nums* is at the time it is called.

melpomene
  • 84,125
  • 8
  • 85
  • 148
0

Here's another look simulating dynamic vars. Assume NUMS is a dynamic var bound to '(2 3 5) before calling BAR. NUMS is free within the lambda and is closed over. But, before the lambda returns, NUMS is rebound. This is what happens when a dynamic var falls out of the lexical scope that pushed a new binding onto its little stack of values.

(defun bar ()
  (let ((nums '(1 2 3)))
    (prog1
        (lambda () nums)
      (setf nums '(2 3 5)))))

SBCL will tell you if it has created a closure or not: #<FUNCTION ...> vs #<CLOSURE ...>. Your code does not create a closure because there are no free vars in the body of the lambda.

stirfoo
  • 35
  • 3