Comment 1 for bug 1758275

Revision history for this message
Richard M Kreuter (kreuter) wrote :

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/documentation/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).

If what you're interested in is "local" function names whose bindings do not outlast a definite scope, I propose you have a look at FLET and LABELS [2].

Regards,
Richard

[1] Here is the reference for DEFUN

http://clhs.lisp.se/Body/m_defun.htm

Because DEFUN has a side effect on the global environment, it's quite rare to see it used inside a function definition in Common Lisp. This is one of the stylistic aspects of your code samples on which I'm basing my guess that you are/were new to Common Lisp. (The other is the ``exdenting'' of the consequent clause in your last definition of FOO. Most Common Lisp code indents the ELSE clause of an IF at the same level as the THEN, though this is merely an aesthetic signal, rather than semantic one.)

[2] http://www.lispworks.com/documentation/HyperSpec/Body/s_flet_.htm