Defuns in consequence and alternate parts of the "if" operator get evaluated always
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
SBCL |
Triaged
|
Low
|
Unassigned |
Bug Description
- Observed in:
SBCL 1.3.1.debian
with:
(:SWANK :SB-BSD-
:ANSI-CL :ASH-RIGHT-VOPS :C-STACK-
:COMPARE-
:FP-AND-
:INTEGER-EQL-VOP :INTERLEAVED-
:LITTLE-ENDIAN :MEMORY-
:OS-PROVIDES-
:OS-PROVIDES-POLL :OS-PROVIDES-PUTWC :OS-PROVIDES-
:PACKAGE-
:SB-AFTER-XC-CORE :SB-CORE-
:SB-PACKAGE-LOCKS :SB-SIMD-PACK :SB-SOURCE-
:SB-UNICODE :SB-XREF-
:STACK-
:STACK-
:UNIX :UNWIND-
run at:
Linux 4.4.0-116-generic #140~14.04.1-Ubuntu SMP Fri Feb 16 09:25:20 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
- But still present in:
SBCL 1.4.5
with:
(:64-BIT :64-BIT-REGISTERS :ALIEN-CALLBACKS :ANSI-CL :ASH-RIGHT-VOPS
:C-STACK-
:COMPARE-
:FP-AND-
:IMMOBILE-SPACE :INLINE-CONSTANTS :INTEGER-EQL-VOP :LARGEFILE :LINKAGE-TABLE
:LINUX :LITTLE-ENDIAN :MEMORY-
:OS-PROVIDES-
:OS-PROVIDES-POLL :OS-PROVIDES-PUTWC :OS-PROVIDES-
:PACKAGE-
:RELOCATABLE-HEAP :SB-DOC :SB-EVAL :SB-FUTEX :SB-LDB :SB-PACKAGE-LOCKS
:SB-SIMD-PACK :SB-SOURCE-
:STACK-
:STACK-
:STACK-
:UNDEFINED-
- This code shows the problem:
(defun foo (n)
(if (< n 3)
(defun bar (n) (format t "bar ~a~%" n)))
(bar n))
(foo 2)
bar 2
NIL
(foo 10)
bar 10
NIL
- But this one works correctly:
(defun foo (n)
(if (< n 3)
(defun bar (n) (format t "bar ~a~%" n))))
(foo 10)
NIL
(bar 1) => ERROR
(foo 1)
BAR
* (bar 1)
bar 1
NIL
-This code also shows the problem:
(defun foo (n)
(format t "foo ~a~%" n)
(if (< n 3)
(defun foo (n) (format t "bar ~a~%" n)))
(if (> n 1)
(foo (- n 1))))
(foo 2)
foo 2
WARNING: redefining COMMON-
bar 1
NIL
(foo 10)
foo 10
bar 9
NIL
- And so does this one:
(defun foo (n)
(format t "foo ~a~%" n)
(if (< n 3)
(defun foo (n) (format t "bar ~a~%" n))
(defun foo (n) (format t "baz ~a~%" n)))
(if (> n 1)
(foo (- n 1))))
(foo 2)
foo 2
WARNING: redefining COMMON-
baz 1
NIL
(foo 10)
foo 10
WARNING: redefining COMMON-
baz 9
NIL
Hello Adam,
Please oblige me if I explain things you already understand; there are a few stylistic aspects of your code samples that suggests that you are/were new to Common Lisp when you wrote this bug report, and so I'm attempting to ``level set'' on the relevant details.
[A] I suspect that you might know this, but for clarity, in Common Lisp, DEFUN has a persistent effect on the global environment (see footnote [1] below). In consequence, your first version of FOO,
(defun foo (n)
(if (< n 3)
(defun bar (n) (format t "bar ~a~%" n)))
(bar n))
has the the following meaning: when FOO is called with an argument, N, that's less than 3, define the function BAR in the global environment; and in all cases, invoke BAR on N.
So when you call FOO with N of 2, BAR gets defined and called; when you subsequently call FOO with N of 10, BAR does not get defined again, but its definition remains from the previous invocation of FOO. So there is a function definition for BAR, and it does get called. This is as you showed in your transcript:
(foo 2)
bar 2
NIL
(foo 10)
bar 10
NIL
[B] However, you say about your second version of FOO that "this one works correctly"
--------
(defun foo (n)
(if (< n 3)
(defun bar (n) (format t "bar ~a~%" n))))
(foo 10)
NIL
(bar 1) => ERROR
(foo 1)
BAR
* (bar 1)
bar 1
NIL
--------
That you describe the behavior in this transcript as correct suggests that you're aware that the effect of (DEFUN BAR...) inside FOO persists after FOO returns.
[C] However, the third and fourth code fragments in your bug report indicates that my tentative inference in [B] could be mistaken, though it's hard to say, because the situation is muddied by a separate concern: both of the third and fourth versions of FOO (conditionally) redefine FOO before invoking it recursively. Such code has undefined consequences per the Common Lisp standard section 3.2.2.3 bullet 4:
http:// www.lispworks. com/documentati on/HyperSpec/ Body/03_ bbc.htm
What I believe accounts for the results shown in your third and fourth transcripts is that SBCL attempts to inline the recursive call to FOO, and produces an unexpected result -- but, again, since both the third and fourth versions of FOO have undefined consequences, all results are notionally valid. (There are a couple things you can do in SBCL that will cause the third and fourth versions of FOO to behave in accordance with how you might evaluate these calls in your mind's eye -- declaring FOO NOTINLINE before compiling it, or setting SB-EXT: *EVALUATOR- MODE* to :INTERPRET before evaluating the DEFUNs -- but since that code does not have defined consequences, the fact that these happen to "work" is not required, and in principle subject to change.)
[E] Finally, as to the subject line of your bug report, "Defuns in consequence and alternate parts of the "if" operator get evaluated always", I hope that the above analyses clarify that the behavior you're seeing is not a bug in either IF or DEFUN (or some interaction between them), but rather a mix of standardly defined Common Lisp behavior (the first and second fragments) and undefined consequences in Common Lisp (the third and fourth fragments)....