unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Macro expansion: Why doesn't the invoked macro see (let (variables)) from the invoking one?
@ 2012-02-08 17:26 Alan Mackenzie
  2012-02-08 17:46 ` Macro expansion: Why doesn't the invoked macro see (let (variables))from " Drew Adams
  2012-02-08 18:05 ` Macro expansion: Why doesn't the invoked macro see (let (variables)) from " Tassilo Horn
  0 siblings, 2 replies; 9+ messages in thread
From: Alan Mackenzie @ 2012-02-08 17:26 UTC (permalink / raw)
  To: emacs-devel

Hello, Emacs.

One macro FOO binds a let variable, then invokes another macro BAR.  BAR
doesn't see this let variable.  Why not?  Is there anything I can do
about this?

(defmacro BAR ()
  (message (if (boundp 'asdf) "asdf" "no asdf"))
  '(message "bar"))

(defmacro FOO ()
  (let (asdf)
    `(BAR)))

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 17:26 Macro expansion: Why doesn't the invoked macro see (let (variables)) from the invoking one? Alan Mackenzie
@ 2012-02-08 17:46 ` Drew Adams
  2012-02-08 19:28   ` Alan Mackenzie
  2012-02-08 18:05 ` Macro expansion: Why doesn't the invoked macro see (let (variables)) from " Tassilo Horn
  1 sibling, 1 reply; 9+ messages in thread
From: Drew Adams @ 2012-02-08 17:46 UTC (permalink / raw)
  To: 'Alan Mackenzie', emacs-devel

> (defmacro BAR ()
>   (message (if (boundp 'asdf) "asdf" "no asdf"))
>   '(message "bar"))
> 
> (defmacro FOO () (let (asdf) `(BAR)))
>
> One macro FOO binds a let variable, then invokes another 
> macro BAR.  BAR doesn't see this let variable.  Why not?

That `let' binding is evaluated only when FOO is expanded.  It is not part of
the resulting expansion (which is then evaluated).

> Is there anything I can do about this?

1. Don't use side effects in the macro definition.

A macro just produces a first result (the expansion), which is then evaluated to
produce the final result.  Anything you want to be seen during that latter
evaluation needs to be part of the expansion itself.

The first call to `message' in macro BAR has no effect when the expansion of BAR
is evaluated (it is not part of the expansion).  `(BAR)' is expanded to
`(message "bar")', which is then evaluated to "bar", which is returned.

2. You might be looking for something like this (dunno):

(defmacro FOO () `(let (asdf) ,(BAR)))

When `(FOO)' is expanded, the expansion includes a `let' binding.

This is the result of `(macroexpand '(FOO))':

(let (asdf) "bar")

Not sure what you're really trying to do, though.




^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables)) from the invoking one?
  2012-02-08 17:26 Macro expansion: Why doesn't the invoked macro see (let (variables)) from the invoking one? Alan Mackenzie
  2012-02-08 17:46 ` Macro expansion: Why doesn't the invoked macro see (let (variables))from " Drew Adams
@ 2012-02-08 18:05 ` Tassilo Horn
  1 sibling, 0 replies; 9+ messages in thread
From: Tassilo Horn @ 2012-02-08 18:05 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

Alan Mackenzie <acm@muc.de> writes:

Hi Alan,

> One macro FOO binds a let variable, then invokes another macro BAR.
> BAR doesn't see this let variable.  Why not?  Is there anything I can
> do about this?
>
> (defmacro BAR ()
>   (message (if (boundp 'asdf) "asdf" "no asdf"))
>   '(message "bar"))
>
> (defmacro FOO ()
>   (let (asdf)
>     `(BAR)))

It seems first (FOO) expands into (BAR) and at that time asdf is bound,
but then the expansion of (BAR) is again isolated.

It looks like try to create a macro that creates a different expansion
depending on its context.  I don't think that's a good idea anyway.
Better add an explicit asdf parameter.

Bye,
Tassilo



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 17:46 ` Macro expansion: Why doesn't the invoked macro see (let (variables))from " Drew Adams
@ 2012-02-08 19:28   ` Alan Mackenzie
  2012-02-08 19:52     ` Stefan Monnier
  2012-02-08 20:16     ` Andreas Schwab
  0 siblings, 2 replies; 9+ messages in thread
From: Alan Mackenzie @ 2012-02-08 19:28 UTC (permalink / raw)
  To: Drew Adams; +Cc: emacs-devel

Hello, Drew!

On Wed, Feb 08, 2012 at 09:46:45AM -0800, Drew Adams wrote:
> > (defmacro BAR ()
> >   (message (if (boundp 'asdf) "asdf" "no asdf"))
> >   '(message "bar"))

> > (defmacro FOO () (let (asdf) `(BAR)))

> > One macro FOO binds a let variable, then invokes another 
> > macro BAR.  BAR doesn't see this let variable.  Why not?

> That `let' binding is evaluated only when FOO is expanded.

This is what I want.

> It is not part of the resulting expansion (which is then evaluated).



> > Is there anything I can do about this?

> 1. Don't use side effects in the macro definition.

I need side effects during macro expansion (see below).

> 2. You might be looking for something like this (dunno):

> (defmacro FOO () `(let (asdf) ,(BAR)))

> When `(FOO)' is expanded, the expansion includes a `let' binding.

> This is the result of `(macroexpand '(FOO))':

> (let (asdf) "bar")

> Not sure what you're really trying to do, though.

OK, here it is in grisly detail.  I want to amend define-minor-mode so
that the position of calling the mode hooks can be specified by d-m-m's
invoker.  To do this, the invoker should insert the macro

(run-hooks-here)

at the appropriate place.  During its expansion, run-hooks-here needs to
set a flag for define-minor-mode meaning "hook expansion already done".
Should this flag not get set, d-m-m inserts the hook calls in the default
place.

What I was trying to do looks like this:


(defmacro run-hooks-here ()
   (setq hooks-called t)        <================= flag variable
  `(run-hooks ',hook (if ,mode ',hook-on ',hook-off)))


(defmacro define-minor-mode (....)
....
  (let (... hooks-run)
....
    ,@body                  <================= expand invoker's forms
                            <====== There may be (run-hooks-here) here.

    ,@(unless hooks-run `((run-hooks-here))) <========= test flag


Should run-hooks-here appear in ,@body, it should inhibit the later
expansion of r-h-h.

########################################################################

So, we've got a dynamically scoped language.  run-hooks-here is invoked
from define-minor-mode.  A variable let-bound in the latter should be
dynamically available in the former.  It isn't.

What am I missing here?

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 19:28   ` Alan Mackenzie
@ 2012-02-08 19:52     ` Stefan Monnier
  2012-02-08 20:09       ` Alan Mackenzie
  2012-02-08 20:16     ` Andreas Schwab
  1 sibling, 1 reply; 9+ messages in thread
From: Stefan Monnier @ 2012-02-08 19:52 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Drew Adams, emacs-devel

> (defmacro run-hooks-here ()
>    (setq hooks-called t)        <================= flag variable
>   `(run-hooks ',hook (if ,mode ',hook-on ',hook-off)))

> (defmacro define-minor-mode (....)
> ....
>   (let (... hooks-run)
> ....
>     ,@body                  <================= expand invoker's forms

This comment is wrong: ",@body" just plugs in the `body' without
macro-expanding it.

>                             <====== There may be (run-hooks-here) here.
>     ,@(unless hooks-run `((run-hooks-here))) <========= test flag

You can do that, but you then need to make sure the `body' gets
macro-expanded while the `let' is live, i.e. during the expansion of the
call to `define-minor-mode'.
You can do it with something like

   (defmacro define-minor-mode (....)
   ....
     (let (... hooks-run)
   ....
       ,@(macroexpand-all body) <================= expand invoker's forms
                               <====== There may be (run-hooks-here) here.
       ,@(unless hooks-run `((run-hooks-here))) <========= test flag


But note that this counts as ugly.  We use such tricks in cl-macs.el to
figure out whether `body' uses `return-from' within a `block' (in order
to optimize away the `catch' that's otherwise needed), but it's ugly,
inefficient, and brittle.
An :after-hook (or :late-code or some other name you prefer) is much
better in this regard.


        Stefan



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 19:52     ` Stefan Monnier
@ 2012-02-08 20:09       ` Alan Mackenzie
  2012-02-10  5:23         ` PJ Weisberg
  0 siblings, 1 reply; 9+ messages in thread
From: Alan Mackenzie @ 2012-02-08 20:09 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Drew Adams, emacs-devel

On Wed, Feb 08, 2012 at 02:52:45PM -0500, Stefan Monnier wrote:
> > (defmacro run-hooks-here ()
> >    (setq hooks-called t)        <================= flag variable
> >   `(run-hooks ',hook (if ,mode ',hook-on ',hook-off)))

> > (defmacro define-minor-mode (....)
> > ....
> >   (let (... hooks-run)
> > ....
> >     ,@body                  <================= expand invoker's forms

> This comment is wrong: ",@body" just plugs in the `body' without
> macro-expanding it.

> >                             <====== There may be (run-hooks-here) here.
> >     ,@(unless hooks-run `((run-hooks-here))) <========= test flag

> You can do that, but you then need to make sure the `body' gets
> macro-expanded while the `let' is live, i.e. during the expansion of the
> call to `define-minor-mode'.

Surely it should be done that way anyway?  I think I'm beginning to get a
clue, but why would expansion of macros be delayed?  That seems to
preclude normal healthy use of side effects.

> You can do it with something like

>    (defmacro define-minor-mode (....)
>    ....
>      (let (... hooks-run)
>    ....
>        ,@(macroexpand-all body) <================= expand invoker's forms
>                                <====== There may be (run-hooks-here) here.
>        ,@(unless hooks-run `((run-hooks-here))) <========= test flag


> But note that this counts as ugly.

I wouldn't disagree with that.

> We use such tricks in cl-macs.el to figure out whether `body' uses
> `return-from' within a `block' (in order to optimize away the `catch'
> that's otherwise needed), but it's ugly, inefficient, and brittle.  An
> :after-hook (or :late-code or some other name you prefer) is much
> better in this regard.

I think you're right.  Ah well.  I learnt this afternoon that quoting a
macro invocation

    '(foo-macro)

doesn't stop it being expanded.  I don't think that's in the elisp
manual.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 19:28   ` Alan Mackenzie
  2012-02-08 19:52     ` Stefan Monnier
@ 2012-02-08 20:16     ` Andreas Schwab
  1 sibling, 0 replies; 9+ messages in thread
From: Andreas Schwab @ 2012-02-08 20:16 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Drew Adams, emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> So, we've got a dynamically scoped language.  run-hooks-here is invoked
> from define-minor-mode.

No, run-hooks-here is not invoked, `((run-hooks-here)) is invoked, which
returns '((run-hooks-here)), which is then substituted into the
expansion of define-minor-mode.  If you want run-hooks-here to be
invoked you need to use `(,(run-hooks-here)).

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-08 20:09       ` Alan Mackenzie
@ 2012-02-10  5:23         ` PJ Weisberg
  2012-02-10 16:58           ` Alan Mackenzie
  0 siblings, 1 reply; 9+ messages in thread
From: PJ Weisberg @ 2012-02-10  5:23 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Stefan Monnier, Drew Adams, emacs-devel

On Wed, Feb 8, 2012 at 12:09 PM, Alan Mackenzie <acm@muc.de> wrote:

> I think you're right.  Ah well.  I learnt this afternoon that quoting a
> macro invocation
>
>    '(foo-macro)
>
> doesn't stop it being expanded.  I don't think that's in the elisp
> manual.

Of course it isn't, because it's not true.  Try it yourself:

(defmacro foo-macro ()
  '(message "Foo macro's code has run!"))

(defmacro bar-macro (one two)
  `(list ,one '(foo-macro) ,two))

If you evaluate:
(bar-macro "Hello" "World")

You get
("Hello" (foo-macro) "World")

Whereas if you changed bar-macro to:
(defmacro bar-macro (one two)
  `(list ,one (foo-macro) ,two))

you would get:
("Hello" "Foo macro's code has run!" "World")

Likewise, (foo-macro) evaluates to "Foo macro's code has run!" (and
prints the message), but '(foo-macro) evaluates to (foo-macro),
exactly as you would expect.

-PJ

Gehm's Corrollary to Clark's Law: Any technology distinguishable from
magic is insufficiently advanced.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Macro expansion: Why doesn't the invoked macro see (let (variables))from the invoking one?
  2012-02-10  5:23         ` PJ Weisberg
@ 2012-02-10 16:58           ` Alan Mackenzie
  0 siblings, 0 replies; 9+ messages in thread
From: Alan Mackenzie @ 2012-02-10 16:58 UTC (permalink / raw)
  To: PJ Weisberg; +Cc: Stefan Monnier, Drew Adams, emacs-devel

On Thu, Feb 09, 2012 at 09:23:55PM -0800, PJ Weisberg wrote:
> On Wed, Feb 8, 2012 at 12:09 PM, Alan Mackenzie <acm@muc.de> wrote:

> > I think you're right.  Ah well.  I learnt this afternoon that quoting a
> > macro invocation

> >    '(foo-macro)

> > doesn't stop it being expanded.  I don't think that's in the elisp
> > manual.

> Of course it isn't, because it's not true.  Try it yourself:

> (defmacro foo-macro ()
>   '(message "Foo macro's code has run!"))

> (defmacro bar-macro (one two)
>   `(list ,one '(foo-macro) ,two))

> If you evaluate:
> (bar-macro "Hello" "World")

> You get
> ("Hello" (foo-macro) "World")

> Whereas if you changed bar-macro to:
> (defmacro bar-macro (one two)
>   `(list ,one (foo-macro) ,two))

> you would get:
> ("Hello" "Foo macro's code has run!" "World")

> Likewise, (foo-macro) evaluates to "Foo macro's code has run!" (and
> prints the message), but '(foo-macro) evaluates to (foo-macro),
> exactly as you would expect.

Hmmm.  I wish I could remember exactly what I saw when playing around.
It was something involving macroexpand and a backquote.

Ah well, I'll come across it again, sometime.

Thanks for the correction.

> -PJ

-- 
Alan Mackenzie (Nuremberg, Germany).



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2012-02-10 16:58 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-02-08 17:26 Macro expansion: Why doesn't the invoked macro see (let (variables)) from the invoking one? Alan Mackenzie
2012-02-08 17:46 ` Macro expansion: Why doesn't the invoked macro see (let (variables))from " Drew Adams
2012-02-08 19:28   ` Alan Mackenzie
2012-02-08 19:52     ` Stefan Monnier
2012-02-08 20:09       ` Alan Mackenzie
2012-02-10  5:23         ` PJ Weisberg
2012-02-10 16:58           ` Alan Mackenzie
2012-02-08 20:16     ` Andreas Schwab
2012-02-08 18:05 ` Macro expansion: Why doesn't the invoked macro see (let (variables)) from " Tassilo Horn

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).