From: ·······@wiwi.uni-marburg.de
Subject: N00b-Question: Evalutaion of macro's arguments
Date: 
Message-ID: <c51f7ac4-9fda-4991-8023-51ef16c754ab@r16g2000vbn.googlegroups.com>
Hello,

attention, maybe a stupid n00b question about macro expansion:

I am quite sure I read a solution for my problem (either in Paul
Graham's book or in the new one from Doug Hoyte) but I can't find it
anymore.

I want to have one macro (say foo) which does something with it's
arguments and a second one (say bar) where the arguments to bar are
evaluated like in a normal function.

For example:

(defmacro foo (n)
  (cond
    ((equal n 5) `(quote five))
    (t `(quote unknown))))

> (foo 5)
FIVE
> (foo 10)
UNKNOWN

And now I want a second macro bar which enables me to do the
following:

> (bar 5)
FIVE
> (bar 10)
UNKNOWN
> (defparameter *x* 5)
*x*
> (bar *x*)
FIVE

All versions I came up with like different solutions with lambda() did
not work.

One thing I was able to do was:

(defun baz-expand (n)
  (cond
    ((equal n 5) 'five)
     (t 'unknown)))

(defmacro baz (n)
  `(baz-expand ,n))

> (baz *x*)
FIVE

But that was not quite the solution I was looking for. Is there any
easy weay to encapsulate a macro in a new macro where the arguments to
the new one are evaluated before the macro starts its work?

Kind regards,


Daniel

From: Pascal J. Bourguignon
Subject: Re: N00b-Question: Evalutaion of macro's arguments
Date: 
Message-ID: <7ciqjaoodg.fsf@pbourguignon.anevia.com>
·······@wiwi.uni-marburg.de writes:
> I want to have one macro (say foo) which does something with it's
> arguments and a second one (say bar) where the arguments to bar are
> evaluated like in a normal function.

If you want all the arguments evaluated, then you shouldn't write a
macro.

(defun bar (n)
  (cond
    ((equal n 5) 'five)
    (t           'unknown)))

(defmacro foo (n) `(quote ,(bar n)))

(list (foo (+ 2 3)) (foo 5) (foo *x*) (foo '*x*)
      (bar (+ 2 3)) (bar 5) (bar *x*) (bar '*x*))
--> (UNKNOWN FIVE UNKNOWN UNKNOWN 
     FIVE    FIVE FIVE    UNKNOWN)


> But that was not quite the solution I was looking for. Is there any
> easy weay to encapsulate a macro in a new macro where the arguments to
> the new one are evaluated before the macro starts its work?

Not really.  The problem is that the first macro cannot evaluate the
arguments at macroexpansion time, and the second macro cannot expand
at run-time.

Well you could make the second macro expand at run-time, but since
EVAL doesn't take an environemnt, you couldn't have the resulting form
be evaluated in the same environment where the second macro was
called.  So in general, it wouldn't work.  If that was really needed,
it would be better to use two functions anyways, generate the code at
run-time, call COMPILE and FUNCALL to have it run in an environment
you could define at run-time.


-- 
__Pascal Bourguignon__
From: Kaz Kylheku
Subject: Re: N00b-Question: Evalutaion of macro's arguments
Date: 
Message-ID: <20090617055307.130@gmail.com>
On 2009-06-05, ·······@wiwi.uni-marburg.de <·······@wiwi.uni-marburg.de> wrote:
> Hello,
>
> attention, maybe a stupid n00b question about macro expansion:
>
> I am quite sure I read a solution for my problem (either in Paul
> Graham's book or in the new one from Doug Hoyte) but I can't find it
> anymore.
>
> I want to have one macro (say foo) which does something with it's
> arguments and a second one (say bar) where the arguments to bar are
> evaluated like in a normal function.
>
> For example:
>
> (defmacro foo (n)
>   (cond
>     ((equal n 5) `(quote five))
>     (t `(quote unknown))))
>
>> (foo 5)
> FIVE
>> (foo 10)
> UNKNOWN
>
> And now I want a second macro bar which enables me to do the
> following:
>
>> (bar 5)
> FIVE
>> (bar 10)
> UNKNOWN
>> (defparameter *x* 5)
> *x*
>> (bar *x*)
> FIVE

  (defmacro bar (expr)
    (typecase expr
      ;; expr is a symbol (including nil) or a compound.
      ;; Generate code to evaluate it and classify at run time.
      ((or cons symbol) `(if (equal ,expr 5) 'five 'unknown))
      ;; expression is some other atom: test it at macro time
      (t (if (equal expr 5) ''five ''unknown))))

Test:

  [16]> (macroexpand '(bar 5))
  'FIVE ;
  T
  [17]> (macroexpand '(bar 3))
  'UNKNOWN ;
  T
  [18]> (macroexpand '(bar *x*))
  (IF (EQUAL *X* 5) 'FIVE 'UNKNOWN) ;
  T

You're better off writing an inline function. Lisp compilers do
constant folding just fine:

  (declaim (inline bar))

  (defun bar (val)
    (if (equal val 5) 'five 'unknown))

A call like (bar 5) will inline the equivalent of (if (equal 5 5) ...)
which a good compiler will optimize to just (quote five).

The next tool to use, if the compiler can't optimize some aspects of
your function as you would like, is to use compiler macros.

Suppose you have an expensive and complicated function, ecf:

  (defun ecf (x)
    ...)

It's a property of ecf that (ecf 42) is 0.  But there is no way that
the compiler can figure this property out from the code; it's the result
of some mathematical proof. Moreover, you just want to stick in 
a test like:

  (defun ecf (x)
    (if (eql x 42) 
      0
      ...))

because the function still has to be called (and the function is
large, you don't want to inline the whole thing just to have this test
optimized!)

What you can do is write a compiler macro to cover the function.
The compiler macro should test whether the argument is a constant
expression, and if so whether it evaluates to 42. In that case,
the compiler macro should return the code 0. Otherwise, it should
return the original source code of the function call:

 (define-compiler-macro ecf (x &whole form)
   (if (and (constantp x) (eql (eval x) 42))
     0
     form))

So now when code like (ecf (+ 40 2)) is compiled, it reduces to 0
at compile time. Yet ecf is neither a macro, nor an inline function.

> But that was not quite the solution I was looking for. Is there any
> easy weay to encapsulate a macro in a new macro where the arguments to
> the new one are evaluated before the macro starts its work?

In general, the arguments of a macro cannot be evaluated before the macro
starts its work, because the arguments refer to run-time values, and the macro
does its work prior to run time.

A macro can recognize constant expressions, of course, and reduce them to
their values.

Of course, in the body of a macro you can pass arguments to the eval
function to try to evaluate them. This will blow up when you have situations
like:

  (let ((value (list 3)))
    (macro value))

The variable called value does not exist at macro-expansion time.
So this kind of macro has a funny behavior which has to be documented,
e.g. ``warning: MACRO evaluates its argument at macro expansion time.''.

To use the macro with a variable, you'd have to make sure that the variable
is defined inside the compiler.

 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defparameter *variable* 42))

 ;; ...

 (macro *variable*)

If you don't have that eval-when, then when the file is being compiled,
the compiler will translate the DEFPARAMETER form into a compiled
variable definition, without actually defining the variable.
When macro wants to evaluate *variable* during expansion, it will find
that *variable* does not exist. (Remember, macro is executing inside
the compiler's image at compile time).  So we have to tell the compiler: hey,
this variable will have to be known at compile time too! Don't just
compile it, but define it for yourself.