1

I wrote the following function

(defun test (name)
  (let ((lst (list 'lambda '()
                   '(let ((slot name))
                     nil))))

     (setf (car (cdr (car (car (cdr (car (cdr (cdr lst)))))))) name)

     (let ((anonymous-function (eval lst)))
       (setf (fdefinition name) anonymous-function))))

If I run (test 'a), the result is (on CLISP) #<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)>; if I run (test 'b) the result is #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>. And, until here, nothing strange. But when I run (fdefinition 'a) I get #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>; and if I run (fdefinition 'b) I get #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>. Isn't it strange? Shouldn't it be #<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)> the answer to (fdefinition 'a)?

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
Matteo
  • 539
  • 3
  • 9
  • possible duplicate of [Why does this function return a different value every time?](http://stackoverflow.com/questions/8962909/why-does-this-function-return-a-different-value-every-time) – sds Jan 19 '15 at 22:52
  • 1
    Note: (car (cdr (car (car (cdr (car (cdr (cdr lst)))))))) can be written: (cadaar (cdaddr lst)). – fstamour Jan 20 '15 at 22:58

1 Answers1

3

You're modifying literal data (and then using that data as body of the body of a lambda function), and that has undefined consequences. See Unexpected persistence of data for more about that. It is a bit surprising that this is what's happening, but it appears that CLISP is saving the actual source (the value of lst) in the lambda function (rather than compiling it, and not copying it) so when you modify the code block, you see the results change in every lambda function that references the same code block. There's only one instance of the list (let ((slot name)) nil), and it is being shared by all the different lambda functions you're creating. When you modify that single list, all of the lambda functions that use it see the change.

The simplest thing (i.e., the smallest change to your code, but not necessarily the best solution) that you could to do get the results that you want would be to use copy-tree to make a fresh code block:

  (let ((lst (copy-tree (list 'lambda '()
                              '(let ((slot name))
                                nil)))))
    ; ...

However, I think that the best way to handle this would just be to make use of Common Lisp's lexical closures and to avoid the mess with eval at all:

(defun test (name)
  (let ((f (lambda ()
             (let ((slot name))
               nil))))
    (setf (fdefinition name) f)))

CL-USER> (test 'a)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) NIL)>
CL-USER> (test 'b)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) NIL)>

While the results look the same, the functions are different because they capture different lexical environments, and so they do what they are supposed to. E.g., look at what happens if you have the body return the value of the variable:

(defun test (name)
  (let ((f (lambda ()
             (let ((slot name))
               slot))))
    (setf (fdefinition name) f)))

CL-USER> (test 'a)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) SLOT)>
CL-USER> (a)
A
CL-USER> (test 'b)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) SLOT)>
CL-USER> (b)
B
Community
  • 1
  • 1
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • Thank you! But I just tried with the copy-list and the result is the same; I haven't tried the second method yet – Matteo Jan 19 '15 at 22:39
  • 1
    @Matteo Sorry, I had the **copy-list** out too far, so it wasn't copying the whole list. See the edit. – Joshua Taylor Jan 19 '15 at 23:04
  • 3
    `copy-list` is out too far in that version as well. Replacing it with `copy-tree` would be a nice way to avoid the issue. – m-n Jan 20 '15 at 00:55
  • @m-n, yes, you're right. Of course, the better option is still just to use the proper lexical closure – Joshua Taylor Jan 20 '15 at 01:30