<Mirko.Vukovic@gmail.com> wrote:
+---------------
| rpw3@rpw3.org (Rob Warnock) wrote:
| > Richard J. Fateman <fate...@eecs.berkeley.edu> wrote:
| > +---------------
| > | The reason I had no *earmuffs* is that I was typing x and y in
| > | repeatedly for tests, and didn't want to type *x* and *y* so much.
| > +---------------
| >
| > Use DEFLEX to get (well, simulate) top-level lexical variables:
| > http://rpw3.org/hacks/lisp/deflex.lisp
...
| Rob, this is really nitpicking, (and possibly wrong), but I am
| wondering if in your deflex gem one could do
|
| CL-USER> (defmacro deflex2 (var val &optional (doc nil docp))
| (let* ((s0 (symbol-name '#:*storage-for-deflex-var-))
| (s1 (symbol-name var))
| (s2 (symbol-name '#:*))
| (s3 (symbol-package var)) ; BUGFIX [see above]
| (backing-var (intern (concatenate 'string s0 s1 s2) s3)))
| `(prog2
| (defparameter ,backing-var ,val ,(when docp `,doc))
| (define-symbol-macro ,var ,backing-var)
| ,(when docp
| `(setf (documentation ',var 'variable) ,doc)))))
|
| My only problem (i.e. lack of macro expertise) with this is that the
| when forms instead of returning nothing place a NIL in the macro
| expansion. But that does not seem to hurt things.
+---------------
The idea of pushing the IF into the body of the PROGN is certainly
reasonable, and I've done it both ways at various times, but I left
the IF outside in the <http://rpw3.org/hacks/lisp/deflex.lisp> version
because I thought it would be a bit clearer to the newcomer.
However, since you brought it up... ;-} ;-}
First, my plain ,DOC gives identical results as your ,(WHEN DOCP `,DOC)
in the DEFPARAMETER's third arg. And neither solves the problem of the
third arg being always provided regardless of the value of DOC.[1]
Fixing that requires using the same splicing hack used below for
the (SETF (DOCUMENTATION ...)) clause.
Anyway, to the main point: if I were to push the IF down in the PROGN,
I would do it the following way instead, which uses a bit more magic
macro syntax but doesn't require the out-of-order thinking that your PROG2
introduced (and also doesn't leave orphan NILs anywhere in the code):
(defmacro deflex (var val &optional (doc nil docp))
(let* ((s0 (symbol-name '#:*storage-for-deflex-var-))
(s1 (symbol-name var))
(s2 (symbol-name '#:*))
(s3 (symbol-package var))
(backing-var (intern (concatenate 'string s0 s1 s2) s3)))
`(progn
(defparameter ,backing-var ,val ,@(when doc (list doc)))
,@(when docp
(list `(setf (documentation ',var 'variable) ,doc)))
;; Must be last, so the value of the form is the symbol VAR.
(define-symbol-macro ,var ,backing-var))))
Yes, this version is *slightly* more compact than my original, but
also perhaps a bit harder to figure out.
Note that this splicing trick is quite useful for both optional and
keyword parameters. But you *must* use the LIST and not just QUOTE
the optional value, that is, write ,@(WHEN DOC (LIST DOC)) and not
just ,@(WHEN DOC '(DOC)), since the latter can result in constant
structure being modified, which is a big no-no. Whereas LIST will
allocate a fresh cons that can be cheerfully spliced.
Back to you...
-Rob
[1] For maximum portability one should probably use the splicing hack
on the third arg of DEFPARAMETER, too, since the CLHS defines that arg
to be "a string", *not* "a string or NIL". Fortunately, this doesn't
seem to matter in all the CLs I have access to -- (DEFPARAMETER FOO val)
gives the same result as (DEFPARAMETER FOO val NIL). But I included that fix in the above
version, too.
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607