From: Joshua Taylor on
Hi all,

Is there any kind of standard terminology for describing these two
approaches to macroexpansion:


1) Expand one layer at a time.

(defmacro let** (bindings &body body)
(if (endp bindings)
`(progn ,@body)
(destructuring-bind (binding &rest bindings) bindings
(destructuring-bind (var form)
(if (listp binding) binding (list binding 'nil))
`(let ((,var ,form))
(let** ,bindings
,@body))))))

Macroexpansion expands the outermost let**, but leaves more inside to be
processed:

(macroexpand-1 '(let** ((x 1)
(y (+ 1 x))
(z (+ 1 x y)))
(list x y z)))
(LET ((X 1)) (LET** ((Y (+ 1 X)) (Z (+ 1 X Y))) (LIST X Y Z)))


2) Expand all layers at once.

(defmacro let*** (bindings &body body)
(labels ((expand-let*** (bindings body)
(if (endp bindings)
`(progn ,@body)
(destructuring-bind (binding &rest bindings) bindings
(destructuring-bind (var form)
(if (listp binding) binding (list binding 'nil))
`(let ((,var ,form))
,(expand-let*** bindings body)))))))
(expand-let*** bindings body)))

Macroexpansion expands all the bindings here, and anything in the body
will be expanded later.

(macroexpand-1 '(let*** ((x 1)
(y (+ 1 x))
(z (+ 1 x y)))
(list x y z)))
(LET ((X 1)) (LET ((Y (+ 1 X))) (LET ((Z (+ 1 X Y))) (PROGN (LIST X Y Z)))))


I think that the latter makes avoiding wrapping the body in progns a bit
easier (e.g., a modification to this example that has expand-let***
return a list of forms), and I suppose that in some situations maybe
file compilation could happen a bit quicker (since there would be fewer
distinct calls to macroexpansion functions). I don't expect that these
considerations are all that important, of course.

What I find more interesting are cases where the second approach makes
things that seem to be impossible with the first. The instance that
I've recently come across is that of destructuring binding wherein
equality of subpatterns is to be enforced. E.g., causing some something
like

(destructuring-bind* (op x x) '(+ 1 2) ...)

to fail (since x wouldn't be allowed to take on both non-eql values 1
and 2) is much easier with the second approach to macroexpansion, and
much more difficult (if not impossible) with the first.

In describing this to a colleague, I found myself wondering if there was
a standard terminology for these two approaches. Does anyone know of one?

//JT
From: Tamas K Papp on
On Mon, 21 Jun 2010 16:08:01 -0400, Joshua Taylor wrote:

> Hi all,
>
> Is there any kind of standard terminology for describing these two
> approaches to macroexpansion:
>
>
> 1) Expand one layer at a time.
>
> (defmacro let** (bindings &body body)
> (if (endp bindings)
> `(progn ,@body)
> (destructuring-bind (binding &rest bindings) bindings
> (destructuring-bind (var form)
> (if (listp binding) binding (list binding 'nil))
> `(let ((,var ,form))
> (let** ,bindings
> ,@body))))))
>
> Macroexpansion expands the outermost let**, but leaves more inside to be
> processed:
>
> (macroexpand-1 '(let** ((x 1)
> (y (+ 1 x))
> (z (+ 1 x y)))
> (list x y z)))
> (LET ((X 1)) (LET** ((Y (+ 1 X)) (Z (+ 1 X Y))) (LIST X Y Z)))
>
>
> 2) Expand all layers at once.
>
> (defmacro let*** (bindings &body body)
> (labels ((expand-let*** (bindings body)
> (if (endp bindings)
> `(progn ,@body)
> (destructuring-bind (binding &rest bindings) bindings
> (destructuring-bind (var form)
> (if (listp binding) binding (list binding 'nil))
> `(let ((,var ,form))
> ,(expand-let*** bindings body)))))))
> (expand-let*** bindings body)))
>
> Macroexpansion expands all the bindings here, and anything in the body
> will be expanded later.
>
> (macroexpand-1 '(let*** ((x 1)
> (y (+ 1 x))
> (z (+ 1 x y)))
> (list x y z)))
> (LET ((X 1)) (LET ((Y (+ 1 X))) (LET ((Z (+ 1 X Y))) (PROGN (LIST X Y
> Z)))))
>
>
> I think that the latter makes avoiding wrapping the body in progns a bit
> easier (e.g., a modification to this example that has expand-let***
> return a list of forms), and I suppose that in some situations maybe
> file compilation could happen a bit quicker (since there would be fewer
> distinct calls to macroexpansion functions). I don't expect that these
> considerations are all that important, of course.
>
> What I find more interesting are cases where the second approach makes
> things that seem to be impossible with the first. The instance that
> I've recently come across is that of destructuring binding wherein
> equality of subpatterns is to be enforced. E.g., causing some something
> like
>
> (destructuring-bind* (op x x) '(+ 1 2) ...)
>
> to fail (since x wouldn't be allowed to take on both non-eql values 1
> and 2) is much easier with the second approach to macroexpansion, and
> much more difficult (if not impossible) with the first.
>
> In describing this to a colleague, I found myself wondering if there was
> a standard terminology for these two approaches. Does anyone know of
> one?

I would just call the first approach recursive. The second one also
uses recursion, but not at the level of the macro, just for
implementing the expansion, which you could do very easily with a loop
or mapcar.

I use the recursive approach so often that I have a utility function
in tpapp-utils:

(defmacro define-with-multiple-bindings (macro)
"Define a version of `macro' with multiple arguments, given as a
list. Application of `macro' will be nested. The new name is the
plural of the old one (generated using format)."
(let ((plural (intern (format nil "~aS" macro))))
`(defmacro ,plural (bindings &body body)
,(format nil "Multiple binding version of ~(~a~)." macro)
(if bindings
`(,',macro ,(car bindings)
(,',plural ,(cdr bindings)
,@body))
`(progn ,@body)))))

Eg CFFI has WITH-FOREIGN-POINTER, but lacks the plural form, so when I
need it, I just

(define-with-multiple-bindings with-foreign-pointer)

Best,

Tamas
From: nanothermite911fbibustards on
Watch this:

http://www.youtube.com/watch?v=kTn-w3xjprg&feature=related

and share with others

On Jun 21, 10:03 pm, Tamas K Papp <tkp...(a)gmail.com> wrote:
> On Mon, 21 Jun 2010 16:08:01 -0400, Joshua Taylor wrote:
> > Hi all,
>
> > Is there any kind of standard terminology for describing these two
> > approaches to macroexpansion:
>
> > 1) Expand one layer at a time.
>
> > (defmacro let** (bindings &body body)
> >    (if (endp bindings)
> >      `(progn ,@body)
> >      (destructuring-bind (binding &rest bindings) bindings
> >        (destructuring-bind (var form)
> >            (if (listp binding) binding (list binding 'nil))
> >          `(let ((,var ,form))
> >             (let** ,bindings
> >               ,@body))))))
>
> > Macroexpansion expands the outermost let**, but leaves more inside to be
> > processed:
>
> > (macroexpand-1 '(let** ((x 1)
> >                          (y (+ 1 x))
> >                          (z (+ 1 x y)))
> >                    (list x y z)))
> > (LET ((X 1)) (LET** ((Y (+ 1 X)) (Z (+ 1 X Y))) (LIST X Y Z)))
>
> > 2) Expand all layers at once.
>
> > (defmacro let*** (bindings &body body)
> >    (labels ((expand-let*** (bindings body)
> >               (if (endp bindings)
> >                 `(progn ,@body)
> >                 (destructuring-bind (binding &rest bindings) bindings
> >                   (destructuring-bind (var form)
> >                       (if (listp binding) binding (list binding 'nil))
> >                     `(let ((,var ,form))
> >                        ,(expand-let*** bindings body)))))))
> >      (expand-let*** bindings body)))
>
> > Macroexpansion expands all the bindings here, and anything in the body
> > will be expanded later.
>
> > (macroexpand-1 '(let*** ((x 1)
> >                           (y (+ 1 x))
> >                           (z (+ 1 x y)))
> >                    (list x y z)))
> > (LET ((X 1)) (LET ((Y (+ 1 X))) (LET ((Z (+ 1 X Y))) (PROGN (LIST X Y
> > Z)))))
>
> > I think that the latter makes avoiding wrapping the body in progns a bit
> > easier (e.g., a modification to this example that has expand-let***
> > return a list of forms), and I suppose that in some situations maybe
> > file compilation could happen a bit quicker (since there would be fewer
> > distinct calls to macroexpansion functions).  I don't expect that these
> > considerations are all that important, of course.
>
> > What I find more interesting are cases where the second approach makes
> > things that seem to be impossible with the first.  The instance that
> > I've recently come across is that of destructuring binding wherein
> > equality of subpatterns is to be enforced.  E.g., causing some something
> > like
>
> > (destructuring-bind* (op x x) '(+ 1 2) ...)
>
> > to fail (since x wouldn't be allowed to take on both non-eql values 1
> > and 2) is much easier with the second approach to macroexpansion, and
> > much more difficult (if not impossible) with the first.
>
> > In describing this to a colleague, I found myself wondering if there was
> > a standard terminology for these two approaches.  Does anyone know of
> > one?
>
> I would just call the first approach recursive.  The second one also
> uses recursion, but not at the level of the macro, just for
> implementing the expansion, which you could do very easily with a loop
> or mapcar.
>
> I use the recursive approach so often that I have a utility function
> in tpapp-utils:
>
> (defmacro define-with-multiple-bindings (macro)
>   "Define a version of `macro' with multiple arguments, given as a
> list.  Application of `macro' will be nested.  The new name is the
> plural of the old one (generated using format)."
>   (let ((plural (intern (format nil "~aS" macro))))
>     `(defmacro ,plural (bindings &body body)
>        ,(format nil "Multiple binding version of ~(~a~)." macro)
>        (if bindings
>            `(,',macro ,(car bindings)
>                      (,',plural ,(cdr bindings)
>                                ,@body))
>            `(progn ,@body)))))
>
> Eg CFFI has WITH-FOREIGN-POINTER, but lacks the plural form, so when I
> need it, I just
>
> (define-with-multiple-bindings with-foreign-pointer)
>
> Best,
>
> Tamas