|
|||||
|
|
#11 |
|
|
> [...] > lexical scoping saves me with distinct, nested bindings of the same > symbol. Not always. Consider the following (simplistic) example of providing read-only access to the iteration counter: (defmacro repeat ((counter-reader-name count-form) &body body) (let ((counter-var (gensym))) `(macrolet ((,counter-reader-name () '(identity ,counter-var))) (dotimes (,counter-var ,count-form) ,@body)))) and the following use: (repeat (i 2) (repeat (j 2) (format t "~%~D ~D" (i) (j)))) and then the result from replacing the call to GENSYM with '#:COUNTER. ---V***il. -- V***il Nikolov <vnikolov@poboxes.com> Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes. |
|
|
#12 |
|
|
> Now you should see when you can get problems: when you use the > anonymous symbol for something else than a lexical variable. Anything > that modifies a global state, be it the current package or for example > a hash table where you store the anonymous symbol with some other > data, for reference by other code generated by your macro. > > For example: > > (defmacro defcommand (pattern &body body) > (let ((name #:command)) > `(progn > (defun ,name (args) ,@body) > (push (cons ',pattern ',name) *commands*)))) > > (defmacro parse-command (command) > `(match-case ,command > ,@(mapcar (lambda (p-f) > `(,(car p-f) (,(cdr-pf) ,(collect-vars ,(car p-f))))) > *commands*))) > > (defcommand (take (?x object)) > (detach object) > (attach object (bag *player*))) > > (defcommand (throw (?x object)) > (cond ((containp (bag player) object) > (detach object) > (attach object *ground*)) > (t (error "You don't hold ~A" object)))) > > (loop for command = (read) do > (parse-command command)) Thankyou Pascal, that is a tremendous example. I'm stunned. My own efforts to contrive a need for GENSYM rather than Sharpsign Colon started with special variables (excessive use of special variables is the royal road to toxic symbol clashes, I think). Err, then I got stuck, without a credible scenario. I'm especially impressed because I had toyed with this kind of adventure code quite recently. How should parse command work? I was doing (apply (car command)(cdr command)). I avoided clashes between my commands and functions in the program by having a command package. The functions that implemented the commands started (defun command:take (&rest stuff) etc etc) I was well pleased with this code, so to have your example code show me, in p***ing, a better way has expanded my horizons wonderfully. I've spent a few hours writing a minimal pattern matcher and filling in the details so I can play with running code and soak up the implications. Now I know why GENSYM clutters up my macroexpansions with those irritating numbers. When I look in *COMMANDS* I really want to see: (((HIT MONSTER WITH (X)) . #:HIT-971) ((DROP (X) ON MONSTER) . # ROP-970)((THROW ROCK AT MONSTER) . #:THROW-969) ((THROW (X) AT (Y)) . #:THROW-968)) with numbers to tell me that the THROW symbols are distinct and it is all working right. Having powerful macros to type all the boilerplate for you makes programming fun * (let ((*readtable* (copy-readtable))) (set-macro-character #\newline (get-macro-character #\) )) (loop for command = (progn (format t "~&> ") (read-delimited-list #\newline)) do (parse-command command))) > hit monster with sword The monster snarls at you. > drop anvil on monster You got a step ladder to stand on? > hit monster with rock The monster snarls at you. > throw rock at monster The monster picks you up by the scruff of the neck and drops you into the debugger! Error in function "Top-Level Form": Broken fourth wall. Restarts: 0: [CONTINUE] Return to the dungeon 1: [ABORT ] Return to Top-Level. Alan Crowe Edinburgh Scotland |