From: David Bakhash
Subject: applying macros
Date: 
Message-ID: <cxju36kfvh9.fsf@hawk.bu.edu>
I'd like to figure out the best way to use macros with `apply'.  I
know that's not kosher, but let's take a simple case:

(defun average-function (&rest args)
  (/ (apply #'+ args)
     (length args)))

(defmacro average-macro (&rest args)
  `(/ (+ ,@args)
      (length ,args)))

(setq my-list '(4.0 5.0))


(apply #'average-function my-list)

==> 4.5

(apply #'average-macro my-list)

==> ERROR!  (i.e. can't apply a macro)

so I solve this problem by doing something like this:

(apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)

==> 4.5

Is there a better way?  Is this the standard way to do this?  What are 
other options?

dave

From: Rainer Joswig
Subject: Re: applying macros
Date: 
Message-ID: <joswig-2105980033190001@194.163.195.66>
In article <···············@hawk.bu.edu>, David Bakhash <·····@bu.edu> wrote:

> (apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)

Btw., don't use apply in the general case. There is a limit
on argument lengths.

CALL-ARGUMENTS-LIMIT  
[Constant]
a positive integer that is the upper exclusive bound on the number of
arguments that may be passed to a function.

Don't use (apply #'+ ....

Use (reduce #'+ ....

-- 
http://www.lavielle.com/~joswig/
From: David Bakhash
Subject: Re: applying macros
Date: 
Message-ID: <cxjra1ofs5i.fsf@hawk.bu.edu>
······@lavielle.com (Rainer Joswig) writes:

> > (apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)
> 
> Btw., don't use apply in the general case. There is a limit
> on argument lengths.

> CALL-ARGUMENTS-LIMIT  
> Don't use (apply #'+ ....
> 
> Use (reduce #'+ ....

First of all, I'd LOVE to use reduce.  It's one of my favorite
functions in CL, but I don't know how to use it in lieu of `apply'.
Also, in the implementation of CL I'm using, the CALL-ARGUMENTS-LIMIT
is quite large (16384), so I'm not worried.  But thanks for making me
aware of that!  never knew of it before.

dave
From: Rainer Joswig
Subject: Re: applying macros
Date: 
Message-ID: <67iyuv93.fsf@lise.lavielle.com>
David Bakhash <·····@bu.edu> writes:

> ······@lavielle.com (Rainer Joswig) writes:
> 
> > > (apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)
> > 
> > Btw., don't use apply in the general case. There is a limit
> > on argument lengths.
> 
> > CALL-ARGUMENTS-LIMIT  
> > Don't use (apply #'+ ....
> > 
> > Use (reduce #'+ ....
> 
> First of all, I'd LOVE to use reduce.  It's one of my favorite
> functions in CL, but I don't know how to use it in lieu of `apply'.

Command: (apply #'+ '(1 2 3 4))
10
Command: (reduce #'+ '(1 2 3 4))
10

In this example there is not much difference. The problem with
apply may appear when you want to do it this way:

Command: (apply #'+ 1 2 3 '(4))
10

> Also, in the implementation of CL I'm using, the CALL-ARGUMENTS-LIMIT
> is quite large (16384), so I'm not worried.

Actually I think 16384 is rather small. You cannot
depend on this number in portable code. On my Symbolics Lisp machine
the value of CALL-ARGUMENTS-LIMIT is 50.
From: Rainer Joswig
Subject: Re: applying macros
Date: 
Message-ID: <joswig-2105980052280001@194.163.195.66>
In article <···············@hawk.bu.edu>, David Bakhash <·····@bu.edu> wrote:


> (defun average-function (&rest args)
>   (/ (apply #'+ args)
>      (length args)))

Get rid of apply. Don't use &REST arguments. If you
really need &REST, try to see if you can
declare the list to have dynamic extent.


(defun average-function (list)
  (assert list (list)
          "List should not be empty.")
  (loop for i from 0
        for item in list
        sum item into result
        finally (return (/ result i))))

> (defmacro average-macro (&rest args)
>   `(/ (+ ,@args)
>       (length ,args)))

Don't do double evaluation of ARGS.

(average-macro (this-function-takes-for-ever-and-has-side-effects))

Expands to:

(/ (+ (THIS-FUNCTION-TAKES-FOR-EVER-AND-HAS-SIDE-EFFECTS))
   (LENGTH ((THIS-FUNCTION-TAKES-FOR-EVER-AND-HAS-SIDE-EFFECTS))))

> (setq my-list '(4.0 5.0))
> 
> 
> (apply #'average-function my-list)

better: (average-function my-list)

> (apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)
> 
> ==> 4.5
> 
> Is there a better way?  Is this the standard way to do this?  What are 
> other options?
> 
> dave

(defmacro average-macro (list-arg)
  `(loop with list = ,list-arg
         initially (assert list (list)
                           "List should not be empty.")
         for i from 0
         for item in list
         sum item into result
         finally (return (/ result i))))


(mapcar #'(lambda (list)
            (average-macro list))
        '((1 2 3) (4 5 6)))

Another option is inlining. The compiler may not do inlining, though.

-- 
http://www.lavielle.com/~joswig/
From: Scott L. Burson
Subject: Re: applying macros
Date: 
Message-ID: <3563E77B.1B37ADEA@zeta-sqoft.com>
Rainer Joswig wrote:
> 
> In article <···············@hawk.bu.edu>, David Bakhash <·····@bu.edu> wrote:
> 
> > (defmacro average-macro (&rest args)
> >   `(/ (+ ,@args)
> >       (length ,args)))
> 
> Don't do double evaluation of ARGS.

There's a worse problem: he's missing a quote.  (LENGTH ,ARGS) will
expand to something like (LENGTH (1.0 2.0 ...)) which is obviously
unevaluable.  What he really wants is to count the args at expansion
time: `(/ (+ ,@args) ,(length args))

(I'm not saying this really the right approach -- I'm only debugging the
approach he's taken.)

-- Scott

				  * * * * *

To use the email address, remove all occurrences of the letter "q".
From: David Bakhash
Subject: Re: applying macros
Date: 
Message-ID: <cxjaf8bexrb.fsf@hawk.bu.edu>
"Scott L. Burson" <·····@zeta-sqoft.com> writes:

> Rainer Joswig wrote:
> > 
> > In article <···············@hawk.bu.edu>, David Bakhash <·····@bu.edu> wrote:
> > 
> > > (defmacro average-macro (&rest args)
> > >   `(/ (+ ,@args)
> > >       (length ,args)))
> > 
> > Don't do double evaluation of ARGS.
> 
> There's a worse problem: he's missing a quote.  (LENGTH ,ARGS) will
> expand to something like (LENGTH (1.0 2.0 ...)) which is obviously
> unevaluable.  What he really wants is to count the args at expansion
> time: `(/ (+ ,@args) ,(length args))

You are right, but your fix is wrong.  What I really wanted was

(length ',args)

thanks,
dave
From: Scott L. Burson
Subject: Re: applying macros
Date: 
Message-ID: <35650E55.58EEE8E@zeta-sqoft.com>
David Bakhash wrote:
> 
> "Scott L. Burson" <·····@zeta-sqoft.com> writes:
> 
> > Rainer Joswig wrote:
> > >
> > > In article <···············@hawk.bu.edu>, David Bakhash <·····@bu.edu> wrote:
> > >
> > > > (defmacro average-macro (&rest args)
> > > >   `(/ (+ ,@args)
> > > >       (length ,args)))
> > >
> > > Don't do double evaluation of ARGS.
> >
> > There's a worse problem: he's missing a quote.  (LENGTH ,ARGS) will
> > expand to something like (LENGTH (1.0 2.0 ...)) which is obviously
> > unevaluable.  What he really wants is to count the args at expansion
> > time: `(/ (+ ,@args) ,(length args))
> 
> You are right, but your fix is wrong.  What I really wanted was
> 
> (length ',args)

That was my first thought too, but why not count them at expansion time
as I suggested?

-- Scott

				  * * * * *

To use the email address, remove all occurrences of the letter "q".
From: Barry Margolin
Subject: Re: applying macros
Date: 
Message-ID: <_oj91.7$hF2.3580@cam-news-reader1.bbnplanet.com>
In article <···············@hawk.bu.edu>, David Bakhash  <·····@bu.edu> wrote:
>"Scott L. Burson" <·····@zeta-sqoft.com> writes:
>> time: `(/ (+ ,@args) ,(length args))
>
>You are right, but your fix is wrong.  What I really wanted was
>
>(length ',args)

It's guaranteed that the two expressions are equivalent.  The latter may
result in slower code, though, if the compiler doesn't do good constant
folding (some compiler implementors do constant folding for simple
arithmetic, like (+ 0 n) == n and (* 0 n) == 0, but don't go much further).
The former simply does the constant folding in the macro definition, so you
don't have to depend on the compiler being smart.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
From: Steve Long
Subject: Loop Constructs (Was Re: applying macros)
Date: 
Message-ID: <3565DB5E.167E@boeing.com>
> (defmacro average-macro (list-arg)
>   `(loop with list = ,list-arg
>          initially (assert list (list)
>                            "List should not be empty.")
>          for i from 0
>          for item in list
>          sum item into result
>          finally (return (/ result i))))

Where can I find some "how to" info on these loop constructs?

Thanks,

steve Long
knowledge based product definition
boeing
From: Stig Hemmer
Subject: Re: Loop Constructs (Was Re: applying macros)
Date: 
Message-ID: <ekvwwbdorc5.fsf@ra.pvv.ntnu.no>
Steve Long <··············@boeing.com> writes:
> Where can I find some "how to" info on these loop constructs?

http://www.harlequin.com/education/books/HyperSpec/Body/sec_6-1.html


Stig Hemmer.
From: Kent M Pitman
Subject: Re: applying macros
Date: 
Message-ID: <sfwyavw6vzt.fsf@world.std.com>
David Bakhash <·····@bu.edu> writes:

> I'd like to figure out the best way to use macros with `apply'. 

The basic reason this is not done is that macros have an
&WHOLE option, and that in APPLY there is no "whole".  In
effect, (apply 'foo '(a b)) splits the form into two peices
that could be cons'd (sort of) to make the whole, but the
cons would produce a new piece of structure each time.

In the olden days, macros used to be a function of a single
form, as in:
 (macro first (x) (rplaca x 'car))
so that once you ran the macro once interpreted, it would "displace"
itself and not be a macro any more.  But the question came up:
what if you do your hack:

 (apply #'(lambda (&rest args) (eval `(average-function ,@args))) list)

but average-function  makes side-effects BOTH to the car and the cdr of the
form.  First, we have to be REALLY clear on the side-effects and sharing, so
let's assume you've written:

  (eval (cons 'average-function list))

That means that some form (average-function foo bar baz) will be side-effectd
perhaps producing (average-internal baz foo bar) BY SIDE-EFFECT.  That MIGHT
mean that if re-evaluated, we'll get (cons 'average-function  list) where
list is now (BAZ FOO BAR) and it might mean now it will on second evaluation
produce (average-internal BAR BAZ FOO).  This might be ok for commutative
functions but not so good if they are not. :-)

Modern macros mostly don't do this, but it's not strictly forbidden
for historical reasons (even though, frankly, I can't figure out why
macros could ever assume they weren't side-effecting constant
structure).  But this is part of the reason that APPLY isn't available.

The other reason is just that macros tend to be compile-time things
and functions run-time things.  So APPLY sort of confuses that.

All in all, I recommend
 (defun average-args-transformer (args)
   `(/ (+ ,@args) (length ,args)))
 (defmacro average-macro (&rest args)
   (average-args-transformer args))
and then if you need it elsewhere, replace
 (apply 'average-macro (cons 'dummy args))
with 
 (eval (average-args-transformer args))

Or if you're operating on a whole form,
 (defun average-transformer (form)
   (destructuring-bind (ignore . args) form
     `(/ (+ ,@args) (length ,args))))
and then
 (eval (average-transformer `(average-macro 4.0 5.0)))

But either way, I think you oughtn't expect EVAL to be
called implicitly.  It's always good to see the EVAL there.

Conceptually, though:
You  MIGHT be better off with an inline function here, btw.
e.g.,
 (declaim (inline average-function))
 (defun average-function (&rest args)
   (/ (apply #'+ args) (length args)))
A really good compiler might actually be able to optimize this
back to something efficient when called normally.  And when
called with APPLY, the thing you want would happen.
This is semantically what CL prefers; but I know some compilers
might not be up to it in practice.