Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a lambda return a global variable and not the local variable?

Tags:

common-lisp

Test #1

I have a globally declared variable f, and a function with argument named f:

(defvar f 1)

(defun test (f)
    f)

I call the function and it returns the value of the argument:

(test 2)
=> 2

Test #2

I again have a globally declared variable f, and a function with argument named f. This time, however, the function returns a lambda, which returns f:

(defvar f 1)

(defun test (f)
     #'(lambda ()
         f))

I call the function and it returns the lambda function. Then I call the lambda function and it returns the value of the global f:

(funcall (test 2))
=> 1

I am surprised. I thought the lambda function is a closure and would return the local f, not the global f. How do I modify the test function and/or the lambda function so that the lambda function returns the local f, not the global f?

A pointer to an online resource that discusses this particular scoping issue would be appreciated.

like image 513
Roger Costello Avatar asked Jan 26 '26 07:01

Roger Costello


1 Answers

By using defvar you are declaring f a special (aka dynamically bound) variable. f in your code from there on are no longer lexically closed but in fact the same as the global variable momentarily changed to 2.

Because of this feature lispers are not happy about global variables without their *earmuffs*. Once they have *earmuffs* it's much easier to see it:

(defvar *f* 1)                       ; special variable *f*
(defun test (*f*)                    ; momentarily rebind *f*
     (format nil "*f* is ~a~%" *f*)  ; use new value
     #'(lambda ()                    ; return lambda using *f*
         *f*))                       ; *f* goes back to being 1
(funcall (test 2))                   ; ==> 1 (prints "*f* is 2\n")

So the lesson is: Never make global variables without *earmuffs* since you will get crazy runtime errors which are almost impossible to detect. This naming convention isn't just for fashion!

As for documentation the hyperspec actually shows how dynamic variables work in the examples for defparameter and defvar. See that they call (foo) => (P V) and that foo re-binds *p* and *v* during its call to bar and, since they are dynamically bound, bar uses the altered values.

like image 125
Sylwester Avatar answered Jan 29 '26 11:01

Sylwester