From: Bob Evans
Subject: Best way to write a particular macro
Date: 
Message-ID: <1145433355.753188.96380@g10g2000cwb.googlegroups.com>
Hi,

I was working through the exercises in Paul Graham's ANSI Common Lisp
and am not sure that I have the best answer for the following problem.
The use of symbol-value seems like it is inelegant. Is there a better
way to do this?

The problem of course, is that if I don't get the symbol-value or use
eval, then n is what the macro actually uses, not the let-bound value
to n of 2.

Any alternatives are greatly appreciated.

Thanks,
Bob Evans

The Exercise:
Define a macro that takes a number, n, followed by one or more
expressions, and returns the value of the nth expression. Note, start
counting at 1.
Example:
(let ((n 2))
      (nth-expression n (/ 1 0) (+ 1 2) (/ 1 0)))
=> 3

Solution:
(defmacro nth-expr (n &body body)
  (nth (1- (symbol-value n)) body))

Another solution:
(defmacro nth-expr (n &body body)
  (nth (1- (eval n)) body))

From: David Sletten
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <R5n1g.5514$543.5501@tornado.socal.rr.com>
Bob Evans wrote:

> Hi,
> 
> I was working through the exercises in Paul Graham's ANSI Common Lisp
> and am not sure that I have the best answer for the following problem.
> The use of symbol-value seems like it is inelegant. Is there a better
> way to do this?
> 
> The problem of course, is that if I don't get the symbol-value or use
> eval, then n is what the macro actually uses, not the let-bound value
> to n of 2.
> 
> Any alternatives are greatly appreciated.
> 
> Thanks,
> Bob Evans
> 
> The Exercise:
> Define a macro that takes a number, n, followed by one or more
> expressions, and returns the value of the nth expression. Note, start
> counting at 1.
> Example:
> (let ((n 2))
>       (nth-expression n (/ 1 0) (+ 1 2) (/ 1 0)))
> => 3
> 
> Solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (symbol-value n)) body))
> 
> Another solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (eval n)) body))
> 

Graham's book is a very popular training ground. Many exercises in the 
book have already been discussed:
http://groups.google.com/group/comp.lang.lisp/search?group=comp.lang.lisp&q=graham+exercise+nth-expr&qt_g=1&searchnow=Search+this+group

Aloha,
David Sletten
From: Bob Evans
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <1145469164.262246.134310@g10g2000cwb.googlegroups.com>
Thanks for the pointer.
From: Bob Evans
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <1145469568.650742.17120@z34g2000cwc.googlegroups.com>
thanks for the pointer
From: Wolfram Fenske
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <1145469193.133263.136000@g10g2000cwb.googlegroups.com>
"Bob Evans" <·······@gmail.com> writes:

> [...]
>
> The Exercise:
> Define a macro that takes a number, n, followed by one or more
> expressions, and returns the value of the nth expression. Note, start
> counting at 1.
> Example:
> (let ((n 2))
>       (nth-expression n (/ 1 0) (+ 1 2) (/ 1 0)))
> => 3
>
> Solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (symbol-value n)) body))
>
> Another solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (eval n)) body))

These are not really solutions.  I think you need to understand better
how macros work.  I'll try to explain: A simplified model of macros
would be to imagine them like functions with two important
differences:

  1. Their arguments are not evaluated.  You could also say they are
     implicitly wrapped in a (quote ...) before they're called.

     Example:

     You define a macro

       (defmacro foo (n)
         (append n '(3)))

     and call it like this:

       (foo (+ 1 2))

     If "foo" were a function, its parameter "n" would be bound to the
     _value_ of the expression (+ 1 2), i. e. 3.  But since "foo" is a
     macro, the parameter "n" is bound to the _list_ (+ 1 2).

  2. The result of the macro call gets evaluated.

Example:

  (defmacro my-when (condition &rest body)
    `(if ,condition
       (progn ,@body)))

  [...]

  (my-when (test)
    (do-this)
    (do-that))

is basically the same as:

  (defun my-when (condition &rest body)
    `(if ,condition
       (progn ,@body)))

  [...]

  (eval
    (my-when '(test)
      '(do-this)
      '(do-that)))

In reality, it's a little different: before anything gets executed,
there's a macro-expansion phase during which the compiler replaces
each call to a macro with the return-value of the macro call.  After
all macro-expansion is done, the transformed source code is
interpreted/compiled.

Hope this helps.


Regards
Wolfram
From: Thomas A. Russ
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <ymik69lwf6e.fsf@sevak.isi.edu>
"Bob Evans" <·······@gmail.com> writes:

> The Exercise:
> Define a macro that takes a number, n, followed by one or more
> expressions, and returns the value of the nth expression. Note, start
> counting at 1.
> Example:
> (let ((n 2))
>       (nth-expression n (/ 1 0) (+ 1 2) (/ 1 0)))
> => 3
> 
> Solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (symbol-value n)) body))
> 
> Another solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (eval n)) body))

The problem you have with these solutions is that you are doing the NTH
operation at macro-expansion time.  You don't want to be doing that,
since it would require that N to have a known value at that time.  That
will then cause things to fail in your test example, especially if you
change the value of N.

What you want your macro to do is to produce the code you would need to
have in order to get the effect that you want.

I find that it is often helpful to start writing macros by starting with
the end result for a concrete example and then working backward to
produce a template for that code.

For example, for a really simple macro that just doubles its argument I
might decide that the code I want is:
    (* 2 arg)
so that means that I want to have the macro return a list like that.  So
I would then use backquote to produce something like the following
template
    `(* 2 ,arg)
and write the entire macro like this:
   (defmacro double (arg)
     `(* 2 ,arg))

I would then use it like
   (double (1+ 8))  => 18

I could examine the results of macroexpansion by using the MACROEXPAND-1
function.  It is very, very useful for working with macro design,
especially since it doesn't evalute the resulting expansion, thus letting
you test things like the second expansion:

   (macroexpand-1 '(double (1+ 8)))  =>   (* 2 (1+ 8))
   (macroexpand-1 '(double y)))      =>   (* 2 Y)

Remember that the results of macro expansion will subsequently be
evaluated.

-Tom.

(Oh, by the way, the example of (double (1+ 8)) is deviously chosen to
 make an implementation of the double macro like
    (defmacro double-error (arg)
       `(+ ,arg ,arg))
 fail.)

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Wolfram Fenske
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <1145478164.885252.263910@j33g2000cwa.googlegroups.com>
···@sevak.isi.edu (Thomas A. Russ) writes:
> (Oh, by the way, the example of (double (1+ 8)) is deviously chosen to
>  make an implementation of the double macro like
>     (defmacro double-error (arg)
>        `(+ ,arg ,arg))
>  fail.)

This isn't very devious because "1+" is a function without
side-effects ;-) A better example would be:

  (let ((a 8))
    (double (incf a)))

"incf" does modify its argument, so that with the wrong implementation
of "double," this evaluates to 19 instead of 18.


Wolfram
From: Bob Evans
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <1145515495.500866.72620@t31g2000cwb.googlegroups.com>
Thanks for the responses everyone. I think I have a much clearer lock
on it now. For a moment, I had slipped into the world of evaluation at
expansion time instead of at runtime.
From: Thomas A. Russ
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <ymizmiev1ix.fsf@sevak.isi.edu>
"Wolfram Fenske" <·····@gmx.net> writes:

> ···@sevak.isi.edu (Thomas A. Russ) writes:
> > (Oh, by the way, the example of (double (1+ 8)) is deviously chosen to
> >  make an implementation of the double macro like
> >     (defmacro double-error (arg)
> >        `(+ ,arg ,arg))
> >  fail.)
> 
> This isn't very devious because "1+" is a function without
> side-effects ;-) A better example would be:
> 
>   (let ((a 8))
>     (double (incf a)))
> 
> "incf" does modify its argument, so that with the wrong implementation
> of "double," this evaluates to 19 instead of 18.

Oops.  I was thinking of INCF and wrote 1+ by mistake.  And in a
supposed trap, too.

How embarrassing.

I'll just slink away now....

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Bourguignon
Subject: Re: Best way to write a particular macro
Date: 
Message-ID: <87slo9g7qf.fsf@thalassa.informatimago.com>
"Bob Evans" <·······@gmail.com> writes:

> Hi,
>
> I was working through the exercises in Paul Graham's ANSI Common Lisp
> and am not sure that I have the best answer for the following problem.
> The use of symbol-value seems like it is inelegant. Is there a better
> way to do this?
>
> The problem of course, is that if I don't get the symbol-value or use
> eval, then n is what the macro actually uses, not the let-bound value
> to n of 2.
>
> Any alternatives are greatly appreciated.
>
> Thanks,
> Bob Evans
>
> The Exercise:
> Define a macro that takes a number, n, followed by one or more
> expressions, and returns the value of the nth expression. Note, start
> counting at 1.
> Example:
> (let ((n 2))
>       (nth-expression n (/ 1 0) (+ 1 2) (/ 1 0)))
> => 3
>
> Solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (symbol-value n)) body))

> Another solution:
> (defmacro nth-expr (n &body body)
>   (nth (1- (eval n)) body))

You've got a strange definition of "solution"...


[45]> (defmacro nth-expr (n &body body) (nth (1- (symbol-value n)) body))
NTH-EXPR
[46]> (let ((n 2)) (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))

*** - SYMBOL-VALUE: N has no dynamic value


[48]> (defmacro nth-expr (n &body body) (nth (1- (eval n)) body))
NTH-EXPR
[49]> (let ((n 2)) (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))

*** - EVAL: variable N has no value


The example give: (let ((n 2)) (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))
implies that n _must_ be evaluated at run-time,
therefore all the expressions _must_ be present in the generated code.

An easy way to have an unevaluated expression in code is to put it in
a function that is not called:

(list
   (lambda () (/ 1 0))
   (lambda () (+ 1 2)))

Of course, there are a number of CL operators that hide such
functions, like COND or CASE or ECASE:

(ECASE n
 (1  (/ 1 0))
 (2  (+ 1 2))
 (3  (/ 1 0)))

Only the expression corresponding to the value of n will be evaluated.
So there, you have what the macro should generate...

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

HEALTH WARNING: Care should be taken when lifting this product,
since its mass, and thus its weight, is dependent on its velocity
relative to the user.