Re: unexpectedly "assumed special" warnings
Joshua TAYLOR <tayloj@cs.rpi.edu> writes:
> Hi all,
> Can anyone tell me why I get this warning? (I don't get similar
> warnings under SBCL or CCL.)
>
> ;;;*** Warning in NEGATIVE: #:*NEGATIVE* assumed special
>
> The code follows. I'd have thought that the macroexpansion for 3)
> would be just like the manually coded 2), but I get the warning in 3),
> but not in 2). Does this have something to do with the fact that the
> symbol isn't interned anywhere? In case 3) I can add a (declare
> (special …)) to the function definition that gets rid of the warning,
> but since progn's contents are top level forms, I'd have through that
> the preceding defparameter should have already made the special
> declaration pervasive. Am I missing something?
>
> ;; lookup and maybe store a value in a hash-table (interning)
>
> (defun get-or-set-interned-value (key hash-table value-function)
> (multiple-value-bind (value presentp)
> (gethash key hash-table)
> (if presentp value
> (setf (gethash key hash-table)
> (funcall value-function)))))
>
>
> ;; 1) here's a interner for squares (with no progn)
>
> (defparameter *square* (make-hash-table))
>
> (defun square (x)
> (get-or-set-interned-value
> x *square* #'(lambda () (* x x))))
>
> ;; 2) and an interner for cubes (in a progn)
>
> (progn
> (defparameter *cube* (make-hash-table))
>
> (defun cube (x)
> (get-or-set-interned-value
> x *cube* #'(lambda () (* x x x))))
> ) ; progn
>
> ;; 3) here's a macro for defining interners, but I get an "... assumed
> special" warning
>
> (defmacro definterner (name (key) &body body)
> (let ((hash-table (make-symbol (concatenate 'string "*" (string name) "*"))))
> `(progn
> (defparameter ,hash-table (make-hash-table))
> (defun ,name (,key)
> (get-or-set-interned-value
> ,key ,hash-table #'(lambda () ,@body))))))
>
> (definterner negative (x)
> (- x))
>
> Thanks in advance. I'll use the special declaration for now, but I'd
> like to know why it's necessary.
>
> --
> Joshua Taylor, http://www.cs.rpi.edu/~tayloj/
After the macro expansion ...
> (macroexpand-1 '(definterner negative (x) (- x)))
(PROGN
(DEFPARAMETER #:*NEGATIVE* (MAKE-HASH-TABLE))
(DEFUN NEGATIVE (X)
(GET-OR-SET-INTERNED-VALUE X #:*NEGATIVE* (FUNCTION (LAMBDA NIL (- X))))))
T
.... the two occurrences of #:*negative* are not the same symbol, so the
second one is assumed special (because it was not declared special).
The reader macro #: "introduces an uninterned symbol ... Every time this
syntax is encountered, a distinct uninterned symbol is created."
(http://www.lispworks.com/documentation/HyperSpec/Body/02_dhe.htm)
Your problem is with the use of MAKE-SYMBOL, which "creates and returns
a fresh, uninterned symbol whose name is the given name", i.e. why
*negative* is preceded by #: in the macro expansion.
(http://www.lispworks.com/documentation/HyperSpec/Body/f_mk_sym.htm)
What you want to use is the INTERN function:
(http://www.lispworks.com/documentation/HyperSpec/Body/f_intern.htm)
(defmacro definterner (name (key) &body body)
(let ((hash-table (intern (concatenate 'string "*" (string name) "*"))))
`(progn
(defparameter ,hash-table (make-hash-table))
(defun ,name (,key)
(get-or-set-interned-value
,key ,hash-table #'(lambda () ,@body))))))
HTH.
Nico