4

I am confused/unsure about how the term variable or binding is used. I think my unsureness can be boiled down to three related simple questions.

(let ((hi 'outer))
  (let ((hi 'inner))
    (print hi))
  (print hi))

Question A: In above code, which of the following is right?

  1. There is only one variable. There are two bindings of one variable: an outer binding, and an inner binding.

  2. There are two variables of one name: an outer variable, and an inner variable.

When I read about local variables on the Internet, sometimes the articles seem to choose 1, and sometimes 2. Are the two equally right?

(let ((hi 0))
  (print hi)
  (setq hi 1)
  (print hi)
  (setq hi 2)
  (print hi))

Question B: which of the following is right for the above code?

  1. There is one binding that is being reused.

  2. There are three bindings.

I have never seen any material that uses the word binding in a way that would choose 2 as answer, but on the other hand, one might still think "the name hi is bound three times. Three bindings happened. The code does three bindings." So I am not sure.

(defun fac (n)
  (if (> n 1)
      (* n (fac (1- n)))
    1))
(fac 4)

Question C: when the recursive function is being executed, which is right?

  1. There will be several bindings of one variable at the same time.

  2. There will be several variables of one name at the same time.

This might seem similar to Question A, but question A involves two let forms, each of which is executed just once, while this question is more like one let form that is being executed in several instances at the same time.

Are these questions like angles on the head of a pin? I am wondering about these questions because there are many articles on that famous gotcha about using closures within a loop, and I think understanding those articles require knowing what one variable is and what one binding is.

Le Curious
  • 1,451
  • 1
  • 13
  • 13
  • 1
    Note that answers here concern specifically Common Lisp since you most likely ask about it, but there are also many other Lisp dialects with different scoping rules. E.g. Clojure has both - lexical and dynamic bindings, and also makes distinction between Symbols and Vars. So don't spread any rules to _all_ Lisps. – ffriend Sep 12 '13 at 14:46
  • @ffriend: Common Lisp does also have both lexical and dynamic bindings. The definitions cited in Lars' answer are true for both. In general, you're right, though: details of the definitions of the terms "variable" and "binding" may very between different languages. – Rörd Sep 12 '13 at 17:25

2 Answers2

9

According to the Common Lisp glossary: (other Lisps may or may not differ in terminology)

  • variable: a binding in the ``variable'' namespace.
  • binding: an association between a name and that which the name denotes.
  • assign: to change the value of the variable in a binding that has already been established.

So the answers would be:

  • A: two variables (and two bindings)
  • B: one binding (being assigned two times)
  • C: several bindings (and several variables) of one name
Lars Brinkhoff
  • 13,542
  • 2
  • 28
  • 48
3

I think that Lars Brinkhoff's answer answers this most directly by appealing to the HyperSpec. You also might have a look at Chapter 6. Variables in Peter Seibel's Practical Common Lisp.

However, let's also consider what could we do to test this? One of the advantages of languages with lexical scoping and lexical closures is that the same binding can be shared between closures.

One binding referenced by multiple closures

For instance, we can bind a single variable x (there's no question that there's only one x here) and return two closures that access it:

(defun incrementer-and-getter (value)
  (let ((x value))
    (values (lambda ()
              (setq x (+ 1 x)))
            (lambda () 
              x))))

Then, we can see that they refer to the same binding when we use the closures:

(multiple-value-bind (inc get) 
    (incrementer-and-getter 23)
  (list (funcall get)
        (funcall inc)
        (funcall get)))
; => (23 24 24)

Multiple bindings with nested lets

Now we can do something similar to test how many bindings there are in the cases that you gave:

(defun test2 ()
  (let (get-outer
        set-outer
        get-inner
        set-inner)
    (let ((hi 'outer))
      (setq get-outer (lambda () hi)
            set-outer (lambda (new) (setq hi new)))
      (let ((hi 'inner))
        (setq get-inner (lambda () hi)
              set-inner (lambda (new) (setq hi new)))))
    (values get-outer
            set-outer
            get-inner
            set-inner)))

(multiple-value-bind (get-outer set-outer get-inner set-inner)
    (test2)
  (list (funcall get-outer)             ; retrieve outer
        (funcall get-inner)             ; retrieve inner
        (funcall set-outer 'new-outer)  ; update outer
        (funcall set-inner 'new-inner)  ; update inner
        (funcall get-outer)             ; retrieve outer
        (funcall get-inner)))           ; retrieve inner
; => (OUTER INNER NEW-OUTER NEW-INNER NEW-OUTER NEW-INNER)

The inner and outer bindings are different.

A single binding updated with setq

Now for the multiple setq case:

(defun test3 ()
  (let (get-first
        set-first
        get-second
        set-second)
    (let ((hi 'first))
      (setq get-first (lambda () hi)
            set-first (lambda (new) (setq hi new)))
      (setq hi 'second)
      (setq get-second (lambda () hi)
            set-second (lambda (new) (setq hi new))))
    (values get-first
            set-first
            get-second
            set-second)))
(multiple-value-bind (get-first set-first get-second set-second)
    (test3)
  (list (funcall get-first)
        (funcall get-second)
        (funcall set-first 'new-first)
        (funcall get-first)
        (funcall get-second)
        (funcall set-second 'new-second)
        (funcall get-first)
        (funcall get-second)))
    (multiple-value-bind (get-first set-first get-second set-second)
        (test3)
      (list (funcall get-first)
            (funcall get-second)
            (funcall set-first 'new-first)
            (funcall set-second 'new-second)
            (funcall get-first)
            (funcall get-second)))
; => (SECOND SECOND NEW-FIRST NEW-FIRST NEW-FIRST NEW-SECOND NEW-SECOND NEW-SECOND)

Here, both get-first and get-second return the same value, and both set-first and set-second update that value. The closures close over the same binding.

Each call to a function establishes new bindings

For the recursive case, we have to be a bit sneakier, but we can still check this:

(defparameter *closures* '())

(defun recurse (n)
  (push (lambda () n) *closures*)
  (push (lambda (new) (setq n new)) *closures*)
  (unless (zerop n)
    (recurse (1- n))))

(recurse 1) ; put four closures into *closures*

Now we can take those back out and see what happens:

;; remember we pushed these in, so they're in backwards
;; order, compared to everything else we've done.
(destructuring-bind (set-y get-y set-x get-x)
    *closures*
  (list (funcall get-x)
        (funcall get-y)
        (funcall set-x 'new-x)
        (funcall set-y 'new-y)
        (funcall get-x)
        (funcall get-y)))
; => (1 0 NEW-X NEW-Y NEW-X NEW-Y)

There is a new binding for each invocation of the function, so the closures reference different bindings.

Common confusions in iteration constructs

For what it's worth, it's not too hard to get used to this behavior (if it's at all surprising in the first place). However, even seasoned Lisp veterans can get tripped up on the behavior in iteration constructs. These kind of cases suddenly make it very important to know whether, e.g., do establishes a new binding for each iteration, or whether it updates the same binding. What should the following code print, 10987654321 or 0000000000?

(let ((closures '()))
  (do ((x 10 (1- x)))
      ((zerop x))
    (push (lambda () x) closures))
  (map nil (lambda (closure)
             (print (funcall closure)))
       closures))

In the case of do, it's 0000000000, because (emphasis added):

All the step-forms, if supplied, are evaluated, from left to right, and the resulting values are assigned to the respective vars.

This comes up a lot in loop where it's implementation dependent, but people expect different behavior from other iteration macros, too. See, for instance, this StackOverflow question:

or these threads on comp.lang.lisp:

Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • Speaking of the Practical Common Lisp link, the link says "A single variable can even have multiple bindings at the same time; parameters to a recursive function". Does this mean that the answer to C is "one name, one variable, multiple bindings"? But then the CLHS jargon link in the other answer says that a variable is a binding, and that seems to indicate that the answer to C is "one name, multiple variables, multiple bindings". Or does this just mean that it is probably not a good idea to try to count the number of variables? – Le Curious Sep 13 '13 at 09:41
  • The full quote is "A single variable--the thing you can point to in the program's source code--can have many different bindings during a run of the program. A single variable can even have multiple bindings at the same time; parameters to a recursive function, for example, are rebound for each call to the function." The point that he making is that when you're in a recursive call, the variable (the actual symbol in the source code) has a binding for the inner call at the same time that is has a binding for the outer call. Personally, I think talking about variables like this (as the symbol – Joshua Taylor Sep 13 '13 at 13:37
  • in the source code) isn't particularly useful, and the HyperSpec doesn't use that convention in the glossary, either. It's much more productive to think of bindings for names (in which case Seibel is using the word variable where the HyperSpec uses name). The book isn't perfect, I suppose, but it's pretty good, and I hope that the content, in general, has been useful. – Joshua Taylor Sep 13 '13 at 13:38