From: Peter Seibel
Subject: Complex macros
Date: 
Message-ID: <m3n0helavz.fsf@javamonkey.com>
When writing complex macros, I find myself tending to write lots of
functions that compute various snippets of code and then a single
macro that uses those functions as helpers to generate it's expansion.

I've played around with some of them, converting the helper functions
to macros and then changing the original macro to expand into
something that contains calls to the helper macros.

At least for the ones I've tried both ways, I haven't seen any
dramatic advantage to writing macros one way or the other. I have
noticed:

  - If you use helper functions, you may need to wrap them in an
  eval-when to make them available to the macro, if it is used in the
  same file it's defined in.

  - With helper functions approach, MACROEXPAND'ing the outermost
  macro is more transparent because it actually expands fully rather
  than expanding to a bunch of other macro calls which then have to be
  expanded.

Beyond those two points, are there other benefits for one approach or
the other that I'm missing? How do you tend to divide up the work done
by a complex macro?

-Peter

P.S. Here's an example of what I'm talking about. Below are two
versions of the core of an MD5 implementation. The first one, as you
can see, uses lots of helper functions. The second one has been
converted to use mostly macros.

;; Helper function style

(defun md5-round (fn vars block-var k-start k-increment s-values i-start)
  "Generate the code for one round."
  (loop repeat 16
    for i from i-start
    for k from k-start by k-increment 
    collect (round-step fn vars block-var (mod k 16) (first s-values) i)
    do
    (setf vars (rotate-list-right vars))
    (setf s-values (rotate-list-left s-values)))))

(defun round-step (fn vars block-var k s i)
  "Generate the code for the basic function in one round. Always of
the form 'a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s).'"
  (destructuring-bind (a b c d) vars
    `(setf ,a
      (32-bit+ ,b
       (<<< (32-bit+
             ,a (,fn ,b ,c ,d)
             (aref ,block-var ,k) ,(compute-t i))
        ,s)))))

(defmacro update-from-block (block-var)
  (let ((vars '(a b c d)))
    `(let ((aa a) (bb b) (cc c) (dd d))
      ,@(md5-round 'f vars block-var 0 1 '(7 12 17 22) 1)
      ,@(md5-round 'g vars block-var 1 5 '(5  9 14 20) 17)
      ,@(md5-round 'h vars block-var 5 3 '(4 11 16 23) 33)
      ,@(md5-round 'i vars block-var 0 7 '(6 10 15 21) 49)
      (setf a (32-bit+ a aa))
      (setf b (32-bit+ b bb))
      (setf c (32-bit+ c cc))
      (setf d (32-bit+ d dd)))))


;; Helper macro style

(defmacro update-from-block (block-var)
  (let ((vars '(a b c d)))
    `(let ((aa a) (bb b) (cc c) (dd d))
      (md5-round f ,vars ,block-var 0 1 (7 12 17 22) 1)
      (md5-round g ,vars ,block-var 1 5 (5  9 14 20) 17)
      (md5-round h ,vars ,block-var 5 3 (4 11 16 23) 33)
      (md5-round i ,vars ,block-var 0 7 (6 10 15 21) 49)
      (setf a (32-bit+ a aa))
      (setf b (32-bit+ b bb))
      (setf c (32-bit+ c cc))
      (setf d (32-bit+ d dd)))))

(defmacro md5-round (fn vars block-var k-start k-increment s-values i-start)
  "Generate the code for one round."
  `(progn
    ,@(loop repeat 16
            for i from i-start
            for k from k-start by k-increment 
            collect `(round-step ,fn ,vars ,block-var ,(mod k 16) ,(first s-values) ,i)
            do
            (setf vars (rotate-list-right vars))
            (setf s-values (rotate-list-left s-values)))))

(defmacro round-step (fn (a b c d) block-var k s i)
  "Generate the code for the basic function in one round. Always of
the form 'a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s).'"
  `(setf ,a
    (32-bit+
     ,b
     (<<< (32-bit+ ,a (,fn ,b ,c ,d) (aref ,block-var ,k) ,(compute-t i)) ,s))))




-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp

From: Joe Marshall
Subject: Re: Complex macros
Date: 
Message-ID: <7k8hbv73.fsf@ccs.neu.edu>
Peter Seibel <·····@javamonkey.com> writes:

> When writing complex macros, I find myself tending to write lots of
> functions that compute various snippets of code and then a single
> macro that uses those functions as helpers to generate it's expansion.

I do this as well.
From: Kaz Kylheku
Subject: Re: Complex macros
Date: 
Message-ID: <cf333042.0305230957.67bd907@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> When writing complex macros, I find myself tending to write lots of
> functions that compute various snippets of code and then a single
> macro that uses those functions as helpers to generate it's expansion.
> 
> I've played around with some of them, converting the helper functions
> to macros and then changing the original macro to expand into
> something that contains calls to the helper macros.
> 
> At least for the ones I've tried both ways, I haven't seen any
> dramatic advantage to writing macros one way or the other. 

You can use indirection on the helper functions, but not on macros:

  `(blah ... ,(mapcar #'helper some-list) ...)

If helper is a macro, you can still do this, but less directly,
something like:

  `(blah ... ,(mapcar #'(lambda (item)
                          (helper item) some-list) ...)

This assumes that HELPER is written to such that it evaluates its
parameter, in which case it screams to be an inline function anyway!

Suppose that helper accepts a symbol parameter, as in (HELPER X). And
SOME-LIST is a list of symbols. Now you have a problem because (HELPER
ITEM) means just that. So you need an eval call in there: (eval
`(helper ,item)). Now there is again no reason for HELPER to be a
macro; it has no environment to expand anything into; it is just being
used as a function to compute an expression that is passed to EVAL.

A good reason to write a macro that expands to more macro calls is if
you have some actual macro language that you want to target. Then when
you perform MACROEXPAND-1 on instances of your macro, you obtain an
expression written in the custom macro language which you can
understand more clearly than a full expansion.  In other words, the
inner macros are useful because they delay some of the translation
tasks into another round of macroexpansion, allowing you to intercept
a comprehensive piece of macro-based source code from the first round.
From: Peter Seibel
Subject: Re: Complex macros
Date: 
Message-ID: <m3fzn5l326.fsf@javamonkey.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> A good reason to write a macro that expands to more macro calls is
> if you have some actual macro language that you want to target. Then
> when you perform MACROEXPAND-1 on instances of your macro, you
> obtain an expression written in the custom macro language which you
> can understand more clearly than a full expansion. In other words,
> the inner macros are useful because they delay some of the
> translation tasks into another round of macroexpansion, allowing you
> to intercept a comprehensive piece of macro-based source code from
> the first round.

That makes a lot of sense to me; thanks for helpful way to think about
it.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Kaz Kylheku
Subject: Re: Complex macros
Date: 
Message-ID: <cf333042.0305250713.2afa74eb@posting.google.com>
···@ashi.footprints.net (Kaz Kylheku) wrote in message news:<···························@posting.google.com>...
> tasks into another round of macroexpansion, allowing you to intercept
> a comprehensive piece of macro-based source code from the first round.
    ^^^^^^^^^^^^^

Oops, that should have been ``comprehensible''. Had I said
``comprehensitive'', it would have been a perfect G. W. Bush. :)
From: Nils Goesche
Subject: Re: Complex macros
Date: 
Message-ID: <lyk7ciatq1.fsf@cartan.de>
Peter Seibel <·····@javamonkey.com> writes:

> When writing complex macros, I find myself tending to write lots of
> functions that compute various snippets of code and then a single
> macro that uses those functions as helpers to generate it's
> expansion.
> 
> I've played around with some of them, converting the helper
> functions to macros and then changing the original macro to expand
> into something that contains calls to the helper macros.
> 
> At least for the ones I've tried both ways, I haven't seen any
> dramatic advantage to writing macros one way or the other. I have
> noticed:
> 
>   - If you use helper functions, you may need to wrap them in an
>   eval-when to make them available to the macro, if it is used in the
>   same file it's defined in.

That's why I usually put the helper functions in an FLET or LABELS
form inside the macro definition.  That way you can also directly use
your local gensyms without passing them around, for instance.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0
From: Barry Margolin
Subject: Re: Complex macros
Date: 
Message-ID: <eCqza.6$Dq2.325@paloalto-snr1.gtei.net>
In article <··············@javamonkey.com>,
Peter Seibel  <·····@javamonkey.com> wrote:
>  - With helper functions approach, MACROEXPAND'ing the outermost
>  macro is more transparent because it actually expands fully rather
>  than expanding to a bunch of other macro calls which then have to be
>  expanded.

This could be considered a pro or a con.  Since it expands fully you can
see everything that will happen, but the huge result is likely to be harder
to read.  Seeing the intermediate macros in the expansion is likely to make
it easier to understand, for much the same reason that it was easier to
write the macro that way in the first place.

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Ivan Boldyrev
Subject: Re: Complex macros
Date: 
Message-ID: <cn14qxb8f.ln2@elaleph.borges.cgitftp.uiggm.nsc.ru>
Peter Seibel <·····@javamonkey.com> writes:
>
>       (setf a (32-bit+ a aa))

How do you implement 32-bit+?  Do use just use
(ldb (byte 32 0) (+ a b))?

-- 
Ivan Boldyrev

       Assembly of a Japanese bicycle requires greatest peace of spirit.
From: Peter Seibel
Subject: Re: Complex macros
Date: 
Message-ID: <m3addbljyz.fsf@javamonkey.com>
Ivan Boldyrev <···············@cgitftp.uiggm.nsc.ru> writes:

> Peter Seibel <·····@javamonkey.com> writes:
> >
> >       (setf a (32-bit+ a aa))
> 
> How do you implement 32-bit+?  Do use just use
> (ldb (byte 32 0) (+ a b))?

(defun 32-bit+ (&rest args)
  "Add x and y and toss any bits that overflow 2^32."
  (mask-field (byte 32 0) (apply #'+ args)))

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp