Hi,
I'm trying to learn the essence of macrology, and any help would be
greatly appreciated.
I view a macro as a tool that can perform re-arrangement on the
parse-tree of s-expressions that make up a LISP/Scheme program. Therefore:
* I can understand how the macro expander finds the proper nodes in
the tree, and then performs re-arrangements on it & it's sub-tree(s).
* How come the macro cannot operate on the parent-node's of the nodes
it is directed towards ? Is there any reason why this is and / or
should be prohibited? I don't believe I grokked anything in my
LISP/Scheme references which should preclude this ( or am I sadly
mistaken? ).
Many thanks,
~Tomer
A little schemer/lisper
·······@noshpam.lbl.government wrote in message news:<········································@thar.lbl.gov>...
>
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ? Is there any reason why this is and / or
> should be prohibited? ...
You need to think a little about the way recursive macro expansion
works. By the time you get to macro expanding a subtree, you no
longer have a pointer to the parent node and there's no way to get
one.
-- rar
·······@noshpam.lbl.government writes:
> Hi,
>
> I'm trying to learn the essence of macrology, and any help would be
> greatly appreciated.
>
> I view a macro as a tool that can perform re-arrangement on the
> parse-tree of s-expressions that make up a LISP/Scheme program. Therefore:
>
> * I can understand how the macro expander finds the proper nodes in
> the tree, and then performs re-arrangements on it & it's sub-tree(s).
>
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ? Is there any reason why this is and / or
> should be prohibited? I don't believe I grokked anything in my
> LISP/Scheme references which should preclude this ( or am I sadly
> mistaken? ).
Well, from a design point of view, that might be just a bit too much
power to be used wisely. What you're saying is a call like:
(foo (bar (baz 1 2) 3))
where FOO and BAR are functions and BAZ is a macro could mean
*anything* at all since BAZ could walk up the parse tree and rewrite
the call to FOO. I suppose there might be cases where this would be
useful but it would be hard to use wisely.
From a technical point of view, it's impossible in current Lisps
because because s-expressions are represented as singly-linked lists;
there's no pointer to the parent.
Obviously if one wanted to make different design choices, one could
change the representation, say to a doubly-linked list. (Though then
you might want to think about how having everything be doubly linked
is going to interact with the garbage collector.)
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> From a technical point of view, it's impossible in current Lisps
> because because s-expressions are represented as singly-linked lists;
> there's no pointer to the parent.
Guy Steele mentioned a notation where sexprs could be treated as a
graph:
http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/msg03504.html
Then parent-operating macros would be possible since you can refer to
a parent, no? I can't find reference to this in any book I have
though.
(keywords s-expression symbolic expression sexp s-exp s-expr)
From: james anderson
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <3FE22EF1.5CEC2943@setf.de>
Tayssir John Gabbour wrote:
>
> Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> > From a technical point of view, it's impossible in current Lisps
> > because because s-expressions are represented as singly-linked lists;
> > there's no pointer to the parent.
>
> Guy Steele mentioned a notation where sexprs could be treated as a
> graph:
> http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/msg03504.html
>
> Then parent-operating macros would be possible since you can refer to
> a parent, no? I can't find reference to this in any book I have
> though.
>
it seems very much like http://www.lispworks.com/reference/HyperSpec/Body/02_dho.htm.
it is not clear whether this puts even the stout of heart in a position to
express the relations which such parent operating macros would require. not to
mention the compiler.
...
From: Rahul Jain
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <87r7z1skon.fsf@nyct.net>
james anderson <··············@setf.de> writes:
> it seems very much like http://www.lispworks.com/reference/HyperSpec/Body/02_dho.htm.
Except that it's not, because there is still a syntactic context
established by sharpsign-equals around the whole region where the
sharpsign-sharpsign gets its data from.
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
james anderson <··············@setf.de> wrote in message news:<·················@setf.de>...
> Tayssir John Gabbour wrote:
> > Guy Steele mentioned a notation where sexprs could be treated as a
> > graph:
> > http://www.ai.mit.edu/~gregs/ll1-discuss-archive-html/msg03504.html
> >
> > Then parent-operating macros would be possible since you can refer to
> > a parent, no? I can't find reference to this in any book I have
> > though.
>
> it seems very much like http://www.lispworks.com/reference/HyperSpec/Body/02_dho.htm.
>
> it is not clear whether this puts even the stout of heart in a position to
> express the relations which such parent operating macros would require. not to
> mention the compiler.
It might not be so bad once we have 3dmacs.
From: Ray Dillinger
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <3FE299B9.C16FFD8F@sonic.net>
Tayssir John Gabbour wrote:
>
> james anderson <··············@setf.de> wrote in message news:<·················@setf.de>...
> > it is not clear whether this puts even the stout of heart in a position to
> > express the relations which such parent operating macros would require. not to
> > mention the compiler.
>
> It might not be so bad once we have 3dmacs.
It is interesting though. It raises questions about lexical scope
when a lexical scope encloses its parent. Consider
#1=(let ((x #1#)(y 'foo))
(if x #t #f))
Is this meaningful? (answer: in scheme I think it leads to an
infinite loop, which is an interesting thing to express with
just a let form.) Is there something that's hard to express
with tree syntax that this kind of thing would be useful for?
It raises questions about functional combinations that refer to
themselves, too. For example, what would #1=((lambda (x) x) #1#)
return? (okay, trick question -- as far as I can tell it wouldn't
return at all).
Bear
james anderson <··············@setf.de> wrote in message news:<·················@setf.de>...
> it is not clear whether this puts even the stout of heart in a position to
> express the relations which such parent operating macros would require. not to
> mention the compiler.
Actually, it's not as bad as it sounds. It's like firing your boss.
Often a chaotic and counterproductive action, sometimes it's what the
company needs. For you to fire him and train his replacement. Or
less violently, your boss can be the enlightened type you can strongly
affect to provide value to the company, and you have a good sequence
of protocols so you can work equally but differently, as a team. The
boss, who we now can call a manager, has the role of observing the
forest and managing interactions between workers.
Procedural and OOP styles are also rife with complexity which we no
longer perceive because we've trained ourselves to be blind to "bad
code possibilities," reacting severely or disappointedly when we
notice bad decisions in other peoples' work. These macros would be
handled the same way; we'd invent protocols and exploit only a
fraction of their power.
If the world had evolved to s-expression languages earlier, this no
doubt would have been experimented with and the issues ironed out.
Offtopic, it should be fun to start a company like Google, and have a
professional CEO to manage you. You can influence the CEO through a
board of directors and other structures.
·······@noshpam.lbl.government wrote:
> Hi,
>
> I'm trying to learn the essence of macrology, and any help would be
> greatly appreciated.
I liked the introduction to macros in Paul Graham's "On Lisp" a lot. See
his website for a free download of that book - http://www.paulgraham.com
. I think my own Guide to Lisp has a relatively good section about
macros, I hope. See http://www.pascalcostanza.de
> I view a macro as a tool that can perform re-arrangement on the
> parse-tree of s-expressions that make up a LISP/Scheme program. Therefore:
>
> * I can understand how the macro expander finds the proper nodes in
> the tree, and then performs re-arrangements on it & it's sub-tree(s).
>
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ? Is there any reason why this is and / or
> should be prohibited? I don't believe I grokked anything in my
> LISP/Scheme references which should preclude this ( or am I sadly
> mistaken? ).
Here are some of the things you can do in Common Lisp:
You can do this to a certain extent with compiler macros. Look up
define-compiler-macro. You could define a compiler macro for the parent
node that analyzes its body and makes the necessary modifications.
Another option is to put your code into its own package and shadow the
definitions that it uses and that are defined in other packages.
Another way is to put one single macro call around your whole code, or
at least to sufficiently large sections, and then you can do almost
anything with it before it gets passed to the compiler.
The MOP also gives you a lot of freedom to modify the semantics of a
program.
Pascal
--
Tyler: "How's that working out for you?"
Jack: "Great."
Tyler: "Keep it up, then."
From: james anderson
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <3FE0CC7B.BE40DBFC@setf.de>
·······@noshpam.lbl.government wrote:
>
> Hi,
>
> I'm trying to learn the essence of macrology, and any help would be
> greatly appreciated.
>
> I view a macro as a tool that can perform re-arrangement on the
> parse-tree of s-expressions that make up a LISP/Scheme program. Therefore:
>
> * I can understand how the macro expander finds the proper nodes in
> the tree, and then performs re-arrangements on it & it's sub-tree(s).
"on it & its sub-tree(s)" is imprecise.
one point of view is that a macro expresses a structural interface through the
form of its destructured argument list. any form which uses the respective
macro operator must match that pattern. this also means that any form which
matches the pattern may invoke the macro. as a rule, the internal structure of
components of the interface should not matter.
in keeping with this rule, a macro will likely not operate "on ... its
subtrees". more likely it will establish an environment in which the
compiler's/evaluator's interpretation of the subtrees will have the intended
effect. this means that a macro function treats its argument expressions as
opaque objects and returns some combination of them. that is,
in common-lisp, there is, of course, the &whole argument, but its use is not
the norm.
>
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ? Is there any reason why this is and / or
> should be prohibited? I don't believe I grokked anything in my
> LISP/Scheme references which should preclude this ( or am I sadly
> mistaken? ).
macros tend to be written functionally: they do not, as a rule, modify the
content of their argument forms. they just rearrange, substitute, and wrap
them. in the context of common-lisp, note the discussion in cltl2 section 8.2.
with respect to caching and *macroexpand-hook*, in particular the issues
shared structure and constancy.
there are cases of macros which explicitly expand argument forms and/or the
argument forms' constituents. in those cases, since the inner expansion occurs
in the dynamic context of the outer expansion, it would be possible to
communicate between them, but this is not the rule. in most cases, the outer
expansion has returned before the inner expansion is invoked. which means that
the communication is downwards only, temporally shifted, through macro
definitions and declarations, and the inner expansion cannot presume very much
about the environment in which it occurs.
...
·······@noshpam.lbl.government wrote in message news:<········································@thar.lbl.gov>...
> Hi,
>
> I'm trying to learn the essence of macrology, and any help would be
> greatly appreciated.
>
> I view a macro as a tool that can perform re-arrangement on the
> parse-tree of s-expressions that make up a LISP/Scheme program. Therefore:
>
> * I can understand how the macro expander finds the proper nodes in
> the tree, and then performs re-arrangements on it & it's sub-tree(s).
It does this in a way that follows the normal evaluation rules. Macros
are expressions that are evaluated. It's possible for macroexpansion
to be interleaved with evaluation, even so that macros are re-expanded
each time they are evaluated. Doing this in an interpreter would
actually be very friendly toward interactive development, because
changes to a macro would propagate to existing code. But of course,
macroexpansion can be split off into a separate pass which walks the
code, looks for the macros and calls on the expanders. Looking for the
proper nodes is not hard: they are just lists with the name of a macro
in the first position! What is tricky is knowing whether or not such a
list is in fact a macro call. For example if you have:
(defmacro foo ...)
(let (foo bar) ...)
Clearly, here the sublist (foo bar) is not a macro node. The code
walker has to understand that LET is a special form and treat it
accordingly.
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ?
Because then it wouldn't have the semantics of a function call that
operates on its arguments. It's nice and simple to synthesize the new
tree from just the old tree and its subtrees.
For one thing, how would the macro gain access to the parent? There
would have to be some special mechanism. How much of the parent?
Remember, Lisp trees are hard to navigate backwards. There are no
parent pointers. Given some list, you have no idea whether other lists
point to it. Moreover, there can be many parents, because of
substructure sharing, which happens a lot in Lisp.
(defvar *child-list* (list 1 2 3))
(defvar *parent-one* (list *child-list*))
(defvar *parent-two* (list *child-list*))
If you have the *child-list* object, there is no way to discover
either *parent-one* or *parent-two* without doing an exhaustive search
of the entire address space.
> Is there any reason why this is and / or
> should be prohibited?
It's not prohibited; it's just not part of the design. That doesn't
mean you can never write a macro whose expansion is based on
inheriting information from the surrounding context rather than just
its constituents.
The general approach is to use some more-enclosing macro that
establishes an environment in which information can be communicated
down to some inner macro calls. Typically, these inner macros are
implemented using MACROLET---they are local macros which are only
visible within that environment, because the outermost environment
macro produces an expansion that writes these macrolets around the
embedded code. When this expansion is processed, these inner macros
are then found in the embedded code and expanded. The macros can
communicate with each other through various means, such as dynamic
variables.
If MACROLET isn't powerful enough, you have to roll your own code
walker.
With this trick, you haven't *really* navigated from a macro to a
parent node; you introduced a bigger macro that is wrapped around the
parent material, and which can help you gain access to relevant pieces
of it.
Here is a really simple example. Suppose you want to make a looping
macro which allows (BREAK) to jump out of the loop. Of course Lisp has
good looping macros already, not to mention named blocks for jumping
out of nested contexts. But for educational reasons, suppose we do it
like this:
(defmacro infinite-loop (&body forms)
(let ((out-label (gensym "OUT-"))
(top-label (gensym "TOP-")))
`(macrolet ((break () `(go ,',out-label)))
(tagbody ,top-label ,@forms (go ,top-label) ,out-label))))
So now we have two macros: the outer macro INFINITE-LOOP takes the
forms and wraps them in logic that causes their evaluation to be
repeated with a backwards GO. The expansion of
(infinite-loop (print 42))
looks like this:
(MACROLET ((BREAK NIL '(GO #:OUT-455)))
(TAGBODY #:TOP-456 (PRINT 42) (GO #:TOP-456) #:OUT-455))
The code is placed into a TAGBODY with two generated labels. These
labels are known to the inner macro BREAK because they were inserted
into its body by INFINITE-LOOP expander, so when (BREAK) calls are
expanded, they inherit this information. Material is flowing from the
outer macro to the inner one by way of template insertion, opposite to
the usual bottom-up synthesis that occurs when a macro inserts
parameter material into a template.
In comp.lang.scheme Kaz Kylheku <···@ashi.footprints.net> wrote:
> With this trick, you haven't *really* navigated from a macro to a
> parent node; you introduced a bigger macro that is wrapped around the
> parent material, and which can help you gain access to relevant pieces
> of it.
Concerning that trick, one probably could not use it when
doing short interactive calculations in the REPL, right?
(Not that I'd see any reason to use the trick in the first
place, but just being curious :-)
From: Rahul Jain
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <878yl9sjz3.fsf@nyct.net>
Sampo Smolander <·························@helsinki.fi> writes:
> Concerning that trick, one probably could not use it when
> doing short interactive calculations in the REPL, right?
Er.. what exact "parent context" would the macro modify, then?
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
In comp.lang.scheme Rahul Jain <·····@nyct.net> wrote:
> Sampo Smolander <·························@helsinki.fi> writes:
>> Concerning that trick, one probably could not use it when
>> doing short interactive calculations in the REPL, right?
> Er.. what exact "parent context" would the macro modify, then?
Exactly.
As far as I understood the "trick", it goes something like this:
(defmacro foo ...<here the weird macro is defined>...)
(foo
...some code...
(now-this (can-be-macro-influenced by-this))
...some code...)
But still in the REPL I could not say:
(now-this (can-be-macro-influenced by-this))
Instead I'd have to say:
(foo (now-this (can-be-macro-influenced by-this)))
i.e., to wrap it inside (foo ...) every time I want to use it.
The awkwardness of having to wrap a macro around some code
in a source code file is close to zero, but it can be
more awkward when working in the REPL.
(Then again, changing the way REPL works is an option,
but that's supposed to be a different trick.)
From: Rahul Jain
Subject: Re: A brash newbie question on macrology
Date:
Message-ID: <87zndpqzv8.fsf@nyct.net>
Sampo Smolander <·························@helsinki.fi> writes:
> As far as I understood the "trick", it goes something like this:
>
> (defmacro foo ...<here the weird macro is defined>...)
[...]
I've done plenty of macros (ok, one serious one) like you suggest.
(defmacro foo (blah &body body)
`(macrolet ((do-whatever (foo)
(funcall ,,blah ,foo)))
,@body))
For a useless example.
--
Rahul Jain
·····@nyct.net
Professional Software Developer, Amateur Quantum Mechanicist
·······@noshpam.lbl.government wrote:
> * How come the macro cannot operate on the parent-node's of the nodes
> it is directed towards ? Is there any reason why this is and / or
> should be prohibited?
To add to what has been said so far, macro systems have traditionally
been implemented as rewriting systems that start from the outermost
expression and work their way in to the inner expressions with no
contextual conditions on any given rewrite. One reason for the lack
of contextual conditions is that it's simpler that way.
In Scheme, I believe it should be possible to obtain the effect of
context-sensitive macros by redefining all of the language's special
forms to support a kind of context-passing style for macros. This
is not a technique I would recommend to the inexperienced or faint
of heart, however.
Will