SBCL changes EQness of a local bound function object, even though it is not set?
Asked Answered
L

2

9

Given this example code (from a Reddit /r/lisp question):

(defun next (pos)
  (nth (1+ pos)
       '(0 1 2 3 4 5 6 7 8 9 10)))

(defvar *next* (function next))

(let ((old-next #'next)
      (previous (make-hash-table)))
  (format t "~% 1 EQ? ~a" (eq old-next *next*))
  (defun next (pos)
    (or (gethash pos previous)
        (setf (gethash pos previous) (funcall old-next pos))))
  (format t "~% 2 EQ? ~a" (eq old-next *next*)))

Above establishes a function NEXT. Inside the LET, we keep the old function in OLD-NEXT. Then we redefine the global function NEXT inside the LET.

CCL/CMUCL/GCL/ECL/CLISP/LispWorks/ABCL:

? (load "test.lisp")

 1 EQ? T
 2 EQ? T

Only SBCL (SBCL 1.3.11) has a different result:

* (load "test.lisp")

 1 EQ? T
 2 EQ? NIL

The value of the local variable old-next is no longer eq to the value of the global variable *next*.

Why???

Lousy answered 26/12, 2016 at 10:50 Comment(2)
Did you ask on SBCL's mailing list too?Drexler
@coredump: no, not yetLousy
D
3

The behavior changes whether or not variables are special:

(inspect
  (progn
    (defun next ())
    (let ((old #'next)
          (foo #'next))
      (declare (special foo))
      (defun next () :different)
      (list old foo))))

The object is a proper list of length 2.
0. 0: #<FUNCTION NEXT>
1. 1: #<FUNCTION NEXT {10037694EB}>

The first element refers to the most recent definition, while the second one, obtained from the special variable, is the old definition, as expected. When you remove the special declaration, both references are EQ (and point to the new definition).

Drexler answered 26/12, 2016 at 14:8 Comment(0)
D
2

Looks like SBCL is trying to be smart and optimizes the variable away.

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (defun foobar ()
    (print :bar))
  (funcall old))

Prints

:FOO 
:BAR 

But if you use SETF on the variable,

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (setf old #'foobar)
  (defun foobar ()
    (print :bar))
  (funcall old))

it prints

:FOO 
:FOO 
Deejay answered 26/12, 2016 at 11:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.