Explanation Needed for Nested Macro Definition 
Author Message
 Explanation Needed for Nested Macro Definition

SunOS 3.5, Sun Common Lisp 2.1.1

I was reading through Deborah Tatar's book "A Programmer's Guide to
Common Lisp" and I discovered a bug in the Macro-Defining Macros
section.  Suppose you want to write a macro that, when called with a
symbol as an argument, defines a macro whose name is that symbol and
which expands into a call to PRINT, which prints the symbol.  On page
225 the book states the following function will do it:

Quote:
> (defmacro make-macro-named (name)

    `(defmacro ,name ()
       `(print ',name)))
MAKE-MACRO-NAMED

This doesn't work.  When I macroexpand-1 I get:

Quote:
> (macroexpand-1 '(make-macro-named fish))

(DEFMACRO FISH NIL (BQ-LIST (QUOTE PRINT) (BQ-LIST (QUOTE QUOTE) NAME)))
T

Notice that NAME isn't even evaluated so this is way off.  After much
playing around I finally came up with the following solution which
works:

Quote:
> (defmacro make-macro-named (name)

    `(defmacro ,name ()
       `(print ,'',name)))
MAKE-MACRO-NAMED

When I macroexpand-1 I get:

Quote:
> (macroexpand-1 '(make-macro-named fish))

(DEFMACRO FISH NIL (QUOTE (PRINT (QUOTE FISH))))
T

and to test:

Quote:
> (make-macro-named fish)
FISH
> (fish)

FISH
FISH

I don't understand why this solution works.  Can anyone explain the
process the reader macros go through in this example?



Mon, 05 Oct 1992 12:59:31 GMT  
 Explanation Needed for Nested Macro Definition

Quote:

>SunOS 3.5, Sun Common Lisp 2.1.1

>I was reading through Deborah Tatar's book "A Programmer's Guide to
>Common Lisp" and I discovered a bug in the Macro-Defining Macros
>section.  Suppose you want to write a macro that, when called with a
>symbol as an argument, defines a macro whose name is that symbol and
>which expands into a call to PRINT, which prints the symbol.  On page
>225 the book states the following function will do it:

>> (defmacro make-macro-named (name)
>    `(defmacro ,name ()
>       `(print ',name)))
>MAKE-MACRO-NAMED

>This doesn't work.

Appendix C of the second addition of Common Lisp: the Language has a
good explanation of nested backquote use. The inner ` in the
definition above is probably a typo; there is no need to use an inner
backquote.

(defmacro make-macro-named (name)
  `(defmacro ,name ()
     '(print ',name)))

Does the right thing.
If you really want to use nested backquote, remember that the leftmost
comma or splice goes with the innermost backquote. The above could be
rewritten as:

(defmacro make-macro-named (name)
  `(defmacro ,name ()
     `(print ',',name)))

,',name is the idiom to get at the value of name because that value is
available when the evaluation of the outer backquote is done. Think of
the inner backquoted expression as being "constructed" when the inner
defmacro is evaluated. (This isn't what really happens, but it is less
confusing than trying to think of about all the backquotes being
constructed at once.) When the inner backquote is constructed, There
will be construction like (unquote (quote name)), which evaluates to
name. Then that value is quoted for printing.

Quote:

>Notice that NAME isn't even evaluated so this is way off.  After much
>playing around I finally came up with the following solution which
>works:

>> (defmacro make-macro-named (name)
>    `(defmacro ,name ()
>       `(print ,'',name)))
>MAKE-MACRO-NAMED

This is a different way of doing the same thing. When the inner
backquote is constructed, there will be an expression like
(unquote (quote (quote name))) => (quote name) which is what you wanted.

Quote:

>I don't understand why this solution works.  Can anyone explain the
>process the reader macros go through in this example?

Nested backquotes can be really useful and really confusing. Again, I
strongly recommend reading Appendix C of CLtL2.


"Ah, youth. Ah, statute of limitations."
                -John Waters



Tue, 06 Oct 1992 01:37:14 GMT  
 Explanation Needed for Nested Macro Definition

Quote:

>   > (defmacro make-macro-named (name)
>       `(defmacro ,name ()
>      `(print ,'',name)))
>   MAKE-MACRO-NAMED
> ...
>   I don't understand why this solution works.  Can anyone explain the
>   process the reader macros go through in this example?

Common Lisp the Language (G.L. Steele) 1st ed. p349-350 explains the
rules that define the meaning of backquoted forms. For the purpose of
this exposition, you may consider the meaning of backquoted forms to
be defined by the following rewrite rules:

R1      `x              ==> 'x when x is neither a list nor a vector
R2      `,x             ==> x
R3      `(x1 x2 ... xn) ==> (APPEND [x1] [x2] ... [xn])

R5      [,xi]           ==> (LIST xi)
R6      [xi]            ==> (LIST `xi)

Here is how your example gets rewritten:

`(DEFMACRO ,NAME NIL `(PRINT ,'',NAME))
  ==> `(DEFMACRO ,NAME NIL (APPEND [PRINT] [,'',NAME])) ;by R3
  ==> `(DEFMACRO ,NAME NIL (APPEND (LIST `PRINT) ;by R6
                                   (LIST '',NAME))) ;by R5
  ==> `(DEFMACRO ,NAME NIL (APPEND (LIST 'PRINT) ;by R1
                                   (LIST '',NAME)))
  ==> (APPEND [DEFMACRO] [,NAME] [NIL]
              [(APPEND (LIST 'PRINT) (LIST '',NAME))]) ;by R3
  ==> (APPEND (LIST `DEFMACRO) ;by R6
              (LIST NAME) ;by R5
              (LIST `NIL) ;by R6
              (LIST `(APPEND (LIST 'PRINT) (LIST '',NAME)))) ;by R6
  ==> (APPEND (LIST 'DEFMACRO) ;by R1
              (LIST NAME)
              (LIST 'NIL) ;by R1
              (LIST (APPEND [APPEND] [(LIST 'PRINT)] [(LIST '',NAME)]))) ;by R3
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND (LIST `APPEND) ;by R6
                            (LIST `(LIST 'PRINT)) ;by R6
                            (LIST `(LIST '',NAME))))) ;by R6
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND (LIST 'APPEND) ;by R1
                            (LIST (APPEND [LIST] ['PRINT])) ;by R3
                            (LIST (APPEND [LIST]
                                          [(QUOTE (QUOTE ,NAME))]))))) ;by R3
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST `LIST)
                                    (LIST `(QUOTE PRINT)))) ;by R3
                      (LIST (APPEND (LIST `LIST)
                                    (LIST `(QUOTE (QUOTE ,NAME))))))));by R3
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST 'LIST) ;by R1
                                    (LIST (APPEND [QUOTE] [PRINT])))) ;by R3
                      (LIST (APPEND
                              (LIST 'LIST) ;by R1
                              (LIST (APPEND [QUOTE]
                                            [(QUOTE ,NAME)]))))))) ;by R3
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST 'LIST)
                                    (LIST (APPEND (LIST `QUOTE) ;by R6
                                                  (LIST `PRINT)))))) ;by R6
                      (LIST (APPEND
                              (LIST 'LIST)
                              (LIST (APPEND
                                      (LIST `QUOTE) ;by R6
                                      (LIST `(QUOTE ,NAME)))))))) ;by R6
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST 'LIST)
                                    (LIST (APPEND (LIST 'QUOTE) ;by R1
                                                  (LIST 'PRINT)))))) ;by R1
                      (LIST (APPEND
                              (LIST 'LIST)
                              (LIST (APPEND
                                      (LIST 'QUOTE) ;by R1
                                      (LIST (APPEND [QUOTE]
                                                    [,NAME])))))))) ;by R3
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST 'LIST)
                                    (LIST (APPEND (LIST 'QUOTE)
                                                  (LIST 'PRINT))))))
                      (LIST (APPEND
                              (LIST 'LIST)
                              (LIST (APPEND
                                      (LIST 'QUOTE)
                                      (LIST (APPEND
                                              (LIST `QUOTE) ;by R6
                                              (LIST NAME))))))))) ;by R5
  ==> (APPEND (LIST 'DEFMACRO) (LIST NAME) (LIST 'NIL)
              (LIST (APPEND
                      (LIST 'APPEND)
                      (LIST (APPEND (LIST 'LIST)
                                    (LIST (APPEND (LIST 'QUOTE)
                                                  (LIST 'PRINT))))))
                      (LIST (APPEND
                              (LIST 'LIST)
                              (LIST (APPEND
                                      (LIST 'QUOTE)
                                      (LIST (APPEND
                                              (LIST 'QUOTE) ;by R1
                                              (LIST NAME)))))))))

Now there are no remaining backquoted forms and we evaluate the form
above in an environment where the variable NAME is bound to the symbol
FISH. In order to better see what's happening, I'll decompose the
process of evaluation into 4 steps:

(APPEND '(DEFMACRO) '(FISH) '(NIL)
  (LIST (APPEND
          '(APPEND)
          (LIST (APPEND '(LIST)
                        (LIST (APPEND '(QUOTE) '(PRINT)))))
          (LIST (APPEND '(LIST)
                        (LIST (APPEND '(QUOTE)
                                (LIST (APPEND '(QUOTE) '(FISH))))))))))

(APPEND '(DEFMACRO) '(FISH) '(NIL)
  (LIST (APPEND '(APPEND)
                (LIST (APPEND '(LIST) (LIST '(QUOTE PRINT))))
                (LIST (APPEND '(LIST) (LIST '(QUOTE (QUOTE FISH))))))))

(APPEND '(DEFMACRO) '(FISH) '(NIL)
  (LIST (APPEND '(APPEND)
                '((LIST (QUOTE PRINT)))
                '((LIST (QUOTE (QUOTE FISH)))))))

(APPEND '(DEFMACRO) '(FISH) '(NIL)
        '((APPEND (LIST (QUOTE PRINT)) (LIST (QUOTE (QUOTE FISH))))))

==> (DEFMACRO FISH NIL (APPEND (LIST 'PRINT) (LIST ''FISH)))

Now evaluating the above expression defines the FISH macro; and
evaluating (FISH) first produces the macro expansion of (FISH), which
is: (PRINT (QUOTE FISH)), and then evaluates it which results in the
symbol FISH being printed.

A better way to define your macro would be:

(DEFMACRO MAKE-MACRO-NAMED (NAME)
  `(DEFMACRO ,NAME ()
     `(PRINT ',',NAME)))

Whenever the idiom ,',form occurs it denotes the value of form
evaluated at the level above the current backquoted level. Memorize it
so you don't have to think about what it means when you need to use
it; I very much doubt that you will ever have to use a more complex
form. Typically:

if FOO is a variable that contains textual data you want to do this:

        `(... `(...    ',',FOO     ...) ...)

if FOO is a variable that contains the name of a variable or an
expression that represents code, you probably want to do this:

        `(... `(...     ,',FOO     ...) ...)

This often happens in macros that use GENSYM to make up new variable
names to use in the piece of code that they expand to.

--Denys



Tue, 06 Oct 1992 03:39:24 GMT  
 
 [ 3 post ] 

 Relevant Pages 

1. Query on macro definitions within macro definitions within...

2. A macro involving two sub-macros - where the 2nd macro needs results from the first

3. description/explanation of CICS macros

4. description/explanation of CICS macros

5. J: Nested explicit definitions: scope of names?

6. nested method definition!!!

7. Nested class definition in C?

8. Q: Definitions enclosed in nested begins?

9. Please Help: Problem with Nested Definitions

10. That nested nested nested if statement...

11. nesting class definitions?

12. nested function definitions and lexical binding

 

 
Powered by phpBB® Forum Software