From: ·············@gmail.com
Subject: Macro trouble
Date: 
Message-ID: <1111684504.145881.150490@g14g2000cwa.googlegroups.com>
I have a macro that returns an expression based on its parameters:

(defmacro test-params (&key param1 param2)
  (if (or param1 param2)
      `(+
        ,@(when param1 `(,param1))
        ,@(when param2 `(,param2)))
      -1))

So when I call (test-params :param1 1 :param2 2) I get returned (+ 1 2)
If I leave off :param1 or :param2 I'll get (+ 1) or (+ 2)
If neither :param1 or :param2 is specified, I'll get -1
This is the way I'd like it to work. Note, this is a contrived example
from a problem I'm working on.

Trouble begins when I try to call test-params from this function:

 (defun hard-part (lst)
  (let ((p1 (cdr (assoc :p1 lst)))
        (p2 (cdr (assoc :p2 lst))))
    (test-params :param1 p1 :param2 p2))))

Idealy, this function would get the parameters from a passed-in assoc
list and call test-params. This does not work (it was not obvious to me
at first, but I see why now). I have it working by using the following
function:

(defun hard-part (lst)
  (let ((p1 (cdr (assoc :p1 lst)))
        (p2 (cdr (assoc :p2 lst))))
    (eval `(test-params :param1 ,p1 :param2 ,p2))))

Somehow I feel this is not the best way. Is there a better/cleaner way
of accomplishing this? Basically I want to call the macro with
parameters that may or may not be specified in an assoc list.

Thanks

From: Harald Hanche-Olsen
Subject: Re: Macro trouble
Date: 
Message-ID: <pcosm2kiza6.fsf@shuttle.math.ntnu.no>
+ ·············@gmail.com:

| I have a macro that returns an expression based on its parameters:
| 
| (defmacro test-params (&key param1 param2)
|   (if (or param1 param2)
|       `(+
|         ,@(when param1 `(,param1))
|         ,@(when param2 `(,param2)))
|       -1))
| 
| So when I call (test-params :param1 1 :param2 2) I get returned (+ 1 2)
| If I leave off :param1 or :param2 I'll get (+ 1) or (+ 2)
| If neither :param1 or :param2 is specified, I'll get -1
| This is the way I'd like it to work. Note, this is a contrived example
| from a problem I'm working on.
| 
| Trouble begins when I try to call test-params from this function:
| 
|  (defun hard-part (lst)
|   (let ((p1 (cdr (assoc :p1 lst)))
|         (p2 (cdr (assoc :p2 lst))))
|     (test-params :param1 p1 :param2 p2))))
| 
| Idealy, this function would get the parameters from a passed-in assoc
| list and call test-params. This does not work (it was not obvious to me
| at first, but I see why now). I have it working by using the following
| function:
| 
| (defun hard-part (lst)
|   (let ((p1 (cdr (assoc :p1 lst)))
|         (p2 (cdr (assoc :p2 lst))))
|     (eval `(test-params :param1 ,p1 :param2 ,p2))))
| 
| Somehow I feel this is not the best way. Is there a better/cleaner way
| of accomplishing this? Basically I want to call the macro with
| parameters that may or may not be specified in an assoc list.

I don't quite see why you need a macro for this in the first place.
You have probably figured out that test-params doesn't do what you
want unless you pass in an /explicit/ nil, or omit a keyword
parameter, right?  But in your application, what you need to know is
if the parameter is /in fact/ nil or not.  Rewriting it as a function
should help:

(defun test-params (&key param1 param2)
  (cond ((and param1 param2) (+ param1 param2))
	(param1 param1)
	(param2 param2)
	(t -1)))

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Harald Hanche-Olsen
Subject: Re: Macro trouble
Date: 
Message-ID: <pcooed8iwzz.fsf@shuttle.math.ntnu.no>
+ Harald Hanche-Olsen <······@math.ntnu.no>:

| (defun test-params (&key param1 param2)
|   (cond ((and param1 param2) (+ param1 param2))
| 	(param1 param1)
| 	(param2 param2)
| 	(t -1)))

Which could be written more concisely, if perhaps less transparently, as

(defun test-params (&key param1 param2)
  (if (and param1 param2)
      (+ param1 param2)
      (or param1 param2 -1)))

-- 
* Harald Hanche-Olsen     <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
  than thinking does: but it deprives us of whatever chance there is
  of getting closer to the truth.  -- C.P. Snow
From: Kalle Olavi Niemitalo
Subject: Re: Macro trouble
Date: 
Message-ID: <878y4cj173.fsf@Astalo.kon.iki.fi>
·············@gmail.com writes:

> I have a macro that returns an expression based on its parameters:
>
> (defmacro test-params (&key param1 param2)
>   (if (or param1 param2)
>       `(+
>         ,@(when param1 `(,param1))
>         ,@(when param2 `(,param2)))
>       -1))

And then you want (let (x y) (test-params :param1 x :param2 y))
to return -1.  Your current macro makes the decision whether to
return -1 at macroexpand time, when x and y have not yet been
bound.  This can be solved in two ways:

(a) Change the macro so that it checks the nullity of the
parameter forms at run time:

  (defmacro test-params (&key param1 param2)
    `(if (or ,param1 ,param2)
         (apply #'+ `(,@(when ,param1 `(,,param1))
                      ,@(when ,param2 `(,,param2))))
         -1))

However, this evaluates the parameter forms multiple times, which
causes problems if the forms have side effects.

(b) Use a function instead:

  (defun test-params (&key param1 param2)
    (if (or param1 param2)
        (apply #'+ `(,@(when param1 `(,param1))
                     ,@(when param2 `(,param2))))
        -1))
From: Pascal Costanza
Subject: Re: Macro trouble
Date: 
Message-ID: <3agkquF68agvsU1@individual.net>
Kalle Olavi Niemitalo wrote:
> ·············@gmail.com writes:
> 
> 
>>I have a macro that returns an expression based on its parameters:
>>
>>(defmacro test-params (&key param1 param2)
>>  (if (or param1 param2)
>>      `(+
>>        ,@(when param1 `(,param1))
>>        ,@(when param2 `(,param2)))
>>      -1))
> 
> 
> And then you want (let (x y) (test-params :param1 x :param2 y))
> to return -1.  Your current macro makes the decision whether to
> return -1 at macroexpand time, when x and y have not yet been
> bound.  This can be solved in two ways:
> 
> (a) Change the macro so that it checks the nullity of the
> parameter forms at run time:
> 
>   (defmacro test-params (&key param1 param2)
>     `(if (or ,param1 ,param2)
>          (apply #'+ `(,@(when ,param1 `(,,param1))
>                       ,@(when ,param2 `(,,param2))))
>          -1))
> 
> However, this evaluates the parameter forms multiple times, which
> causes problems if the forms have side effects.
> 
> (b) Use a function instead:
> 
>   (defun test-params (&key param1 param2)
>     (if (or param1 param2)
>         (apply #'+ `(,@(when param1 `(,param1))
>                      ,@(when param2 `(,param2))))
>         -1))

Other suggestions:

(defun test-params (&key (param1 0 param1-p)
                          (param2 0 param2-p))
   (if (or param1-p param2-p)
       (+ param1 param2)
      -1))


(defun test-params (&rest params)
   (declare (dynamic-extent params))
   (if params
       (apply #'+ params)
      -1))


Pascal