Something unclear in the Common Lisp standard
There is what I think is a confusion as to bound declarations in the Common Lisp standard. I may be wrong about this, but I think I’m correct.
Bound and free declarations
Declarations in Common Lisp can be either bound or free:
- a bound declaration appears at the head of a binding form and applies to a variable or function binding made by that form;
- a free declaration is any declaration which is not bound.
There are declarations which do not apply to bindings, such as optimize
: these are always free.
Examples of bound and free declarations
In the form
(let ((x 1))
(declare (type integer x))
...)
the declaration is bound and applies to the binding of x
. In the form
(let ((/x/ 1))
(declare (special /x/)
(optimize (speed 3)))
...)
the special
declaration is bound and applies to the binding of /x/
, while the optimize
declaration is free.
In the form
(let ((x 1))
(locally
(declare (type integer x)
(optimize speed))
...)
...)
Both declarations are free and apply only to the body of the locally
form.
Declarations which may not be ignored
Most declarations may be ignored by the implementation: this is the case for all type declarations, for instance. Two may not be:
notinline
forbids inline compilation of the functions it names;special
requires dynamic bindings to be made when it is bound, and requires references to be to dynamic, not lexical bindigns when it is free.
I’m going to exploit the non-ignorability of special
declarations to show a case where the confusion arises.
The confusion
Forms like let*
bind sequentially:
(let* ((x 1) (y x))
...)
first binds x
and then binds y
to the value of x
. Now, I am not sure of the standard ever says this, but all implementations I have tried take this to mean that the same name can be bound several times by let*
:
(let* ((x 1) (x x))
...)
is legal, if stylistically awful. That’s because the obvious transformation of let*
into nested let
s turns this into:
(let ((x 1))
(let ((x x))
...))
which is clearly fine.
So now we come to the problem: what should this mean?
(let* ((x 1) (x x))
(declare (type fixnum x))
...
Which binding of x
does the declaration apply to? The standard does not say. In this case it might not matter, because this declaration can be ignored, but here is a case where it does matter:
(let (c)
(let* ((/x/ 1)
(/x/ (progn
(setf c (lambda () /x/))
2)))
(declare (special /x/))
(values c (lambda () /x/))))
This expression returns two values, both of which are functions:
- if the first
/x/
is special then calling the first function will result in an error; - if the second
/x/
is special then calling the second function will result in an error.
So using this trick you can know whether the first binding, second binding, or both bindings are affected by the special
declaration.
And, again, the standard does not say which binding is affected, or whether both should be. And implementations differ. Given the following file
(in-package :cl-user)
(defun call-ok-p (f)
(multiple-value-bind (v c)
(ignore-errors
(funcall f)
t)
(declare (ignore c))
v))
(defun ts ()
(multiple-value-bind (one two)
(let (c)
(let* ((/x/ 1)
(/x/ (progn
(setf c (lambda () /x/))
2)))
(declare (special /x/))
(values c (lambda () /x/))))
(values (call-ok-p one)
(call-ok-p two))))
(multiple-value-bind (first-lexical second-lexical) (ts)
(format t "~&first ~:[special~;lexical~]~%~
second ~:[special~;lexical~]~%"
first-lexical second-lexical))
SBCL
first lexical
second special
CCL
first special
second special
LispWorks
first special
second special
What should the answer be?
I think that the interpretation taken by CCL and LispWorks is better: in forms like this declarations should apply to all the bindings made by the form. An alternative answer is that the declarations should apply to the visible bindings at the point of the declaration, which is the approach taken by SBCL.
It’s tempting to say that the obvious rewrite of let*
as nested let
s gives you the SBCL answer, but it does not. In a form like
(let* ((x 3) (y x))
(declare (type integer x)
(type (integer 0) y))
...)
This must be rewritten as
(let ((x 3))
(declare (type integer x))
(let ((y x))
(declare (type (integer 0) y))
...))
So the declaration for x
must be raised out of the inner let
so it remains bound: the implementation already has to do work to get declarations in the right place and can’t just naïvely rewrite the form.
I prefer the first interpretation both because I think it represents what people are likely to want more closely, but also because I think the standard could be interpreted as meaning that without being rewritten.
Does this matter?
Probably only in very obscure cases! I just thought it was interesting.
Thanks to vrious people on the Lisp-HUG mailing list for coming up with this.