From: Erann Gat
Subject: Macroexpansion question
Date: 
Message-ID: <my-first-name.my-last-name-2409031506150001@k-137-79-50-101.jpl.nasa.gov>
I have a macro that expands in one of two ways.  I would like for the
choice of expansions to be made according to the lexical context in which
the macro finds itself.  In other words, I would like the following
behavior:

(with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
(with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)

Is there any way to do that without a full code walker?


Thanks,
E.

From: Erann Gat
Subject: Re: Macroexpansion question
Date: 
Message-ID: <my-first-name.my-last-name-2409031533200001@k-137-79-50-101.jpl.nasa.gov>
In article
<···········································@k-137-79-50-101.jpl.nasa.gov>,
··························@jpl.nasa.gov (Erann Gat) wrote:

> I have a macro that expands in one of two ways.  I would like for the
> choice of expansions to be made according to the lexical context in which
> the macro finds itself.  In other words, I would like the following
> behavior:
> 
> (with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
> (with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)
> 
> Is there any way to do that without a full code walker?

Never mind, I figured it out.

(defmacro with-context (context &body body)
  (let ( (*context* context) )
    `(funcall ',(compile nil (lambda () ,@body)))))

(The compiler *is* a code-walker.  Duh!)

I wonder, can you do that in Scheme?  :-)

E.
From: Paul F. Dietz
Subject: Re: Macroexpansion question
Date: 
Message-ID: <R-Cdned1bP-Nt--iU-KYvA@dls.net>
Erann Gat wrote:

>>I have a macro that expands in one of two ways.  I would like for the
>>choice of expansions to be made according to the lexical context in which
>>the macro finds itself.  In other words, I would like the following
>>behavior:
>>
>>(with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
>>(with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)
>>
>>Is there any way to do that without a full code walker?
> 
> 
> Never mind, I figured it out.
> 
> (defmacro with-context (context &body body)
>   (let ( (*context* context) )
>     `(funcall ',(compile nil (lambda () ,@body)))))
> 
> (The compiler *is* a code-walker.  Duh!)
> 
> I wonder, can you do that in Scheme?  :-)


This isn't how I would do it.

Instead, I'd have WITH-CONTEXT-1 and WITH-CONTEXT-2 expand into
signalling macrolets.  Your macro could detect the presence of this
signalling macrolet by means of an &environment parameter:

(with-context-1 ...) ==>
    (macrolet ((context-indicator () 'context-1))
       ...)
(with-context-2 ...) ==>
    (macrolet ((context-indicator () 'context-2))
       ...)

(defmacro my-macro (&environment env)
    (ecase (macroexpand '(context-indicator) env)
      (context-1 ...)
      (context-2 ...)))

	Paul
From: Erann Gat
Subject: Re: Macroexpansion question
Date: 
Message-ID: <my-first-name.my-last-name-2409031700450001@k-137-79-50-101.jpl.nasa.gov>
In article <······················@dls.net>, "Paul F. Dietz"
<·····@dls.net> wrote:

> Erann Gat wrote:
> 
> >>I have a macro that expands in one of two ways.  I would like for the
> >>choice of expansions to be made according to the lexical context in which
> >>the macro finds itself.  In other words, I would like the following
> >>behavior:
> >>
> >>(with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
> >>(with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)
> >>
> >>Is there any way to do that without a full code walker?
> > 
> > 
> > Never mind, I figured it out.
> > 
> > (defmacro with-context (context &body body)
> >   (let ( (*context* context) )
> >     `(funcall ',(compile nil (lambda () ,@body)))))
> > 
> > (The compiler *is* a code-walker.  Duh!)
> > 
> > I wonder, can you do that in Scheme?  :-)
> 
> 
> This isn't how I would do it.
> 
> Instead, I'd have WITH-CONTEXT-1 and WITH-CONTEXT-2 expand into
> signalling macrolets.  Your macro could detect the presence of this
> signalling macrolet by means of an &environment parameter:
> 
> (with-context-1 ...) ==>
>     (macrolet ((context-indicator () 'context-1))
>        ...)
> (with-context-2 ...) ==>
>     (macrolet ((context-indicator () 'context-2))
>        ...)
> 
> (defmacro my-macro (&environment env)
>     (ecase (macroexpand '(context-indicator) env)
>       (context-1 ...)
>       (context-2 ...)))

So that's what those &environment thingies are for!  I've been wondering
about that for years.  :-)

Thanks!

E.
From: Steven M. Haflich
Subject: Re: Macroexpansion question
Date: 
Message-ID: <Irscb.624$mS1.132762956@newssvr13.news.prodigy.com>
I want to endorse Paul's wisdon here.  Use of macrolets is the right thing, since
macrolets belong to the lexical environment.  Depending on a special variable to
carry contextual information to macros makes aassumptions about how the compiler
happens to work as a code walker, and I believe it is possible to write a code
that works in a way that is not congruent with dynamic state.
From: Erann Gat
Subject: Re: Macroexpansion question
Date: 
Message-ID: <my-first-name.my-last-name-2409032220160001@192.168.1.52>
In article <·······················@newssvr13.news.prodigy.com>, "Steven
M. Haflich" <·················@alum.mit.edu> wrote:

> I want to endorse Paul's wisdon here.  Use of macrolets is the right
thing, since
> macrolets belong to the lexical environment.  Depending on a special
variable to
> carry contextual information to macros makes aassumptions about how the
compiler
> happens to work as a code walker, and I believe it is possible to write a code
> that works in a way that is not congruent with dynamic state.

Yes, that's clear to me now.

Two things I am still curious about.

1.  What else is &environment useful for?

2.  Is this programming technique writte down anywhere?  "On Lisp", which
everyone seems to consider the canonical reference on advanced macros,
says nothing about &environment.

E.
From: Paul F. Dietz
Subject: Re: Macroexpansion question
Date: 
Message-ID: <wz2dndPqL8KJS--iXTWJkw@dls.net>
Erann Gat wrote:

> 1.  What else is &environment useful for?

Standardly, not much else (CONSTANTP, maybe?)

This is an area ripe for extension.  It would be very useful for
(for example) making declaration information available in macros or
compiler macros.  As Duane recently said, this kind of thing was not
added to the standard because it wasn't fully baked.

	Paul
From: Nils Goesche
Subject: Re: Macroexpansion question
Date: 
Message-ID: <lyu1707was.fsf@cartan.de>
··························@jpl.nasa.gov (Erann Gat) writes:

> 1.  What else is &environment useful for?

You sometimes have to pass it on, for instance in
MAKE-LOAD-FORM-SAVING-SLOTS.  And for code-walking, of course, only
that accessing and manipulating the environment objects isn't
portable, unfortunately.

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

PGP key ID 0x0655CFA0
From: Peter Seibel
Subject: Re: Macroexpansion question
Date: 
Message-ID: <m3n0csen7d.fsf@javamonkey.com>
··························@jpl.nasa.gov (Erann Gat) writes:

> In article <·······················@newssvr13.news.prodigy.com>, "Steven
> M. Haflich" <·················@alum.mit.edu> wrote:
> 
> > I want to endorse Paul's wisdon here. Use of macrolets is the
> > right thing, since macrolets belong to the lexical environment.
> > Depending on a special variable to carry contextual information to
> > macros makes aassumptions about how the compiler happens to work
> > as a code walker, and I believe it is possible to write a code
> > that works in a way that is not congruent with dynamic state.
> 
> Yes, that's clear to me now.
> 
> Two things I am still curious about.
> 
> 1.  What else is &environment useful for?

This is not really an answer to your question but since I happened to
have done the work tracking it down, here's a list of (I think) all
the functions in the standard that take an environment object as an
argument:

  Accessor COMPILER-MACRO-FUNCTION
  Accessor MACRO-FUNCTION
  Function CONSTANTP
  Function GET-SETF-EXPANSION
  Function MACROEXPAND
  Function MACROEXPAND-1
  Function MAKE-LOAD-FORM-SAVING-SLOTS
  Function SUBTYPEP
  Function TYPEP
  Function UPGRADED-ARRAY-ELEMENT-TYPE
  Function UPGRADED-COMPLEX-PART-TYPE
  Standard Generic Function MAKE-LOAD-FORM

There are also some X3J13 issues in the HyperSpec that talk about
environments some.

-Peter

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

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Barry Margolin
Subject: Re: Macroexpansion question
Date: 
Message-ID: <RVCcb.46$pd.3@news.level3.com>
In article <·······················@newssvr13.news.prodigy.com>,
Steven M. Haflich <·················@alum.mit.edu> wrote:
>I want to endorse Paul's wisdon here.  Use of macrolets is the right thing, since
>macrolets belong to the lexical environment.

BTW, this is what the old COMPILER-LET was for, but we got rid of it when
we realized that this MACROLET trick could be used to the same end.

-- 
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: Kaz Kylheku
Subject: Re: Macroexpansion question
Date: 
Message-ID: <cf333042.0309242140.3b56c1b6@posting.google.com>
··························@jpl.nasa.gov (Erann Gat) wrote in message news:<···········································@k-137-79-50-101.jpl.nasa.gov>...
> In article
> <···········································@k-137-79-50-101.jpl.nasa.gov>,
> ··························@jpl.nasa.gov (Erann Gat) wrote:
> 
> > I have a macro that expands in one of two ways.  I would like for the
> > choice of expansions to be made according to the lexical context in which
> > the macro finds itself.  In other words, I would like the following
> > behavior:
> > 
> > (with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
> > (with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)
> > 
> > Is there any way to do that without a full code walker?
> 
> Never mind, I figured it out.
> 
> (defmacro with-context (context &body body)
>   (let ( (*context* context) )
>     `(funcall ',(compile nil (lambda () ,@body)))))

I think you have two commas out to one backquote in. This is probably
right:

  `(funcall ,(compile nil `(lambda () ,@body)))

where we are taking advantage of the ability of COMPILE to take a
lambda expression in the place of a closure.

The problem with this approach is that the BODY forms are not
evaluated in the surrounding lexical context, and so the body cannot
refer to surrounding lexical variables.

Yes you have a lambda there, but it won't capture anything, because
after my correction is applied, it becomes obvious that it's just a
dead list that only becomes live code in the bowels of the compiler
subroutine. COMPILE isn't much different from EVAL in this regard.

> (The compiler *is* a code-walker.  Duh!)

My first thought was that MY-MACRO should be a macrolet, so that

   (with-context-1 ...(my-macro) ...)

expands into something like

   (macrolet ((my-macro () ...))
     ... (my-macro) ...)

Of course, the definition of MY-MACRO varies among the different
WITH-CONTEXT-* forms, but if the definitions are similar, the
expanders for these macros can share a lot of common code.

Local macros are your first resource when you suspect you need a code
walker.
From: Erann Gat
Subject: Re: Macroexpansion question
Date: 
Message-ID: <my-first-name.my-last-name-2509030802590001@192.168.1.52>
In article <····························@posting.google.com>,
···@ashi.footprints.net (Kaz Kylheku) wrote:

> ··························@jpl.nasa.gov (Erann Gat) wrote in message
news:<···········································@k-137-79-50-101.jpl.nasa.gov>...
> > In article
> > <···········································@k-137-79-50-101.jpl.nasa.gov>,
> > ··························@jpl.nasa.gov (Erann Gat) wrote:
> > 
> > > I have a macro that expands in one of two ways.  I would like for the
> > > choice of expansions to be made according to the lexical context in which
> > > the macro finds itself.  In other words, I would like the following
> > > behavior:
> > > 
> > > (with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
> > > (with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)
> > > 
> > > Is there any way to do that without a full code walker?
> > 
> > Never mind, I figured it out.
> > 
> > (defmacro with-context (context &body body)
> >   (let ( (*context* context) )
> >     `(funcall ',(compile nil (lambda () ,@body)))))
> 
> I think you have two commas out to one backquote in. This is probably
> right:
> 
>   `(funcall ,(compile nil `(lambda () ,@body)))

Yep.  I'm not sure how that other version got into the post.  It doesn't
even compile.

> where we are taking advantage of the ability of COMPILE to take a
> lambda expression in the place of a closure.
> 
> The problem with this approach is that the BODY forms are not
> evaluated in the surrounding lexical context, and so the body cannot
> refer to surrounding lexical variables.

Hm, good point.

> Local macros are your first resource when you suspect you need a code
> walker.

A good rule to remember.  Thanks.

E.