Home > Archive > Lisp > February 2008 > What's up with Scheme macros?
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
What's up with Scheme macros?
|
|
| Ken Tilton 2008-02-12, 4:32 am |
| While trying to explain my irrational prejudice against Scheme macros I
got curious as to why I hated them, so I googled up this:
http://www.xs4all.nl/~hipster/lib/s...ntax-primer.txt
Boys and girls, that is almost eleven thousand words long!!!!!
I see the problem, they have this whole pattern-matching syntax-rules
language moving from source input to output. I have done (and am
supposed to be doing) enough Prolog to realize Scheme macro writers must
be way smarter than me. But... why? No, not why are they smarter. Why do
they have to be smarter? What was wrong with treating the source as an
ordinary tree of data and just whinging it about with Scheme itself to
produce the output? Please restrict yourself to one or more of these
answers:
1. The hygiene made us do it.
2. We actually thought it would be way and super powerful.
3. We were embarrassd by the small size of the standard and decided to
make it up in the macro language
4. Other _____________________
Jes curious,
kenny
--
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/
"In the morning, hear the Way;
in the evening, die content!"
-- Confucius
| |
| Shiro Kawai 2008-02-12, 4:32 am |
| I use both (CL and Scheme) at work but more inclined to Scheme.
And I do have a mixed feeling about the Scheme macros.
Ken Tilton <kennytil...@optonline.net> wrote:
> 1. The hygiene made us do it.
If I need to pick one from the first three, probably this is it.
Theoretically, hygiene has nothing to do with pattern matching
sublanguage.
The pattern matching part is for the convenience and efficiency, if
I understand correctly. To realize hygiene, every identifier has to
carry the syntactic context, which is added or removed in every macro
expansion.
It would be very inefficient to do that context handling
eagerly if the macro's input form is huge S-expr but the macro
expander
only shuffles its toplevel elements, so you want to do the context
handling lazily---that is, instead of applying the context to every
leaf
of the S-expr, you just feed a tuple of <S-expr, Context> to the macro
expander, which "peels" off the context on demand (e.g. <(a (b c)),
Context>
is transfomed to (<a, Context> <(b c), Context> ) if the expander needs
to see the toplevel elements.) Because of this tupling, you cannot
use
ordinary car/cdr to decompose the input.
The reason I have a mixed feeling is that I do like hygiene (it is

that an exported macro of my module foo expands into a form that
inserts
the reference to the foo's internal macros/procedures and it just
works.
I'd use package prefix in CL, but I feel it's like hack.) but carrying
pattern language is... say, too heavy. Lots of smart people have
tackled
this problem so there may not be the way to avoid that, but I hope
otherwise.
For the meantime, I use both legacy macros and hygienic marcos in my
Scheme code at work; legacy macros are handy if you have total control
over the source so that nobody will step on your toe or vice versa.
| |
| Pascal Costanza 2008-02-12, 8:17 am |
| Ken Tilton wrote:
> While trying to explain my irrational prejudice against Scheme macros I
> got curious as to why I hated them, so I googled up this:
>
> http://www.xs4all.nl/~hipster/lib/s...ntax-primer.txt
>
> Boys and girls, that is almost eleven thousand words long!!!!!
>
> I see the problem, they have this whole pattern-matching syntax-rules
> language moving from source input to output. I have done (and am
> supposed to be doing) enough Prolog to realize Scheme macro writers must
> be way smarter than me. But... why? No, not why are they smarter. Why do
> they have to be smarter? What was wrong with treating the source as an
> ordinary tree of data and just whinging it about with Scheme itself to
> produce the output? Please restrict yourself to one or more of these
> answers:
>
> 1. The hygiene made us do it.
No, there are hygienic macro systems where source is a tree of data.
Actually, in most hygienic macro systems, source is still a tree of data.
> 2. We actually thought it would be way and super powerful.
Lisp-style macros (as in Common Lisp) provide backquote as a convenient
means to construct new s-expressions. One nice thing about backquote is
that you can make backquote expressions look very close the code you
have in your mind. (Compare `(progn ,@body) to (cons 'progn body), for
example.)
Common Lisp also provides destructuring macro lambda lists, where you
can already preprocess the arguments a macro receives by tearing them
apart into subparts. For example: (defmacro with ((&rest bindings) &body
body) ...) instead of (defmacro with (&rest code) ...).
Backquote works pretty well. However, destructuring macro lambda lists
are limited - note how to you can't see the constituents of the bindings
list in the with example. You have to 'manually' tear them apart. (Loop
is pretty good at such things, but you still have to do it.)
If you want to make destructuring more fine-grained, you also have to
make changes to the corresponding construct for reconstructing code
(like backquote). That's where the complexity comes in.
Syntax-rules and syntax-case work quite well in that regard (although I
personally prefer to use LOOP).
> 3. We were embarrassd by the small size of the standard and decided to
> make it up in the macro language
They could have kept the spec smaller by sticking to Lisp-style macros
and adding a rename construct. The other macro systems can be built on
top of that. I guess removing the weaknesses and restrictions that make
additional features appear necessary is not so important after all. :-P
To be more serious: Syntax-rules is a macro system that is very simple
to use for very simple macros. You sometimes want to define your own
macros to illustrate some points in academic papers. Syntax-rules macros
are more than good enough for such purposes, and the macro definitions
can be read by people who are not used to macro programming. That's a
good thing, if the macro is not the main part of what you want to
discuss. Macro definitions in such papers hardly need to be complex (and
can always be simplified for illustration purposes).
You know, there is more you can do with programming languages than
writing applications...
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Jens Axel Soegaard 2008-02-12, 8:17 am |
| Ken Tilton wrote:
> While trying to explain my irrational prejudice against Scheme macros I
> got curious as to why I hated them, so I googled up this:
>
> http://www.xs4all.nl/~hipster/lib/s...ntax-primer.txt
>
> Boys and girls, that is almost eleven thousand words long!!!!!
And "On Lisp" is even longer!!!!!
"Primer" is obviously not to be taken literally.
> I see the problem, they have this whole pattern-matching syntax-rules
> language moving from source input to output. I have done (and am
> supposed to be doing) enough Prolog to realize Scheme macro writers must
> be way smarter than me. But... why? No, not why are they smarter. Why do
> they have to be smarter? What was wrong with treating the source as an
> ordinary tree of data and just whinging it about with Scheme itself to
> produce the output?
The use of pattern matching is a detail.
That's not why Schemers abandoned the old way. There are *two* goals of
modern Scheme macro systems. Hygiene (i.e. automatic renaming) as
another Paris Hilton got all the press, while the referentially
transparent property was left behind in the dust.
Referentially transparent macros?
If a macro transformer inserts a free reference to an identifier,
the reference refers to the binding that was visible where the
transformer was specified, regardless of any local bindings that may
surround the use of the macro.
In short the meaning of the expansion of a macro doesn't depend on where
it is used. In order to implement that you need to attach information
to variables, and thus symbols are replaced by "syntax-objects
representing symbols".
For a CL view on the matters see these posts in a very informative
thread on Scheme macros in comp.lang.lisp in 2004.
Anton van Straaten in comp.lang.lisp:
http://groups.google.com/group/comp...cfdf326287b3d01
Pascal Constanza in comp.lang.lisp:
http://groups.google.com/group/comp...7d80b9d8da8c085
Anyhow, stop reading about syntax-rules (so last millenium) and treat
yourself with some light reading on syntax-case. Dybvig's
Writing Hygenic Macros in Scheme with Syntax-Case
ftp://ftp.cs.indiana.edu/pub/scheme...iucstr356.ps.gz
is a must read.
So is Dybvig's contribution to the book "Beautiful code":
http://www.cs.indiana.edu/~dyb/pubs/bc-syntax-case.pdf
--
Jens Axel Søgaard
| |
| Kjetil S. Matheussen 2008-02-12, 8:17 am |
| On Tue, 12 Feb 2008, Jens Axel Soegaard wrote:
> Ken Tilton wrote:
>
> And "On Lisp" is even longer!!!!!
>
> "Primer" is obviously not to be taken literally.
>
>
> The use of pattern matching is a detail.
>
> That's not why Schemers abandoned the old way.
I wouldn't say that. My impression is that most scheme implementations,
and all the large ones (anyone who doesn't?), support define-macro. I
would actually also be surprised if hygenic macros is more used by
schemeres than "the old way", although I don't have any data to back
that up with. Anyway, I would never use a scheme where you couldn't
write macros the old way.
| |
| andreuri2000@yahoo.com 2008-02-12, 7:25 pm |
| On Feb 12, 5:14 am, Shiro Kawai <shiro.ka...@gmail.com> wrote:
> It would be very inefficient to do that context handling
> eagerly if the macro's input form is huge S-expr but the macro
> expander
> only shuffles its toplevel elements, so you want to do the context
> handling lazily---that is, instead of applying the context to every
> leaf
> of the S-expr, you just feed a tuple of <S-expr, Context> to the macro
> expander, which "peels" off the context on demand (e.g. <(a (b c)),
> Context>
> is transfomed to (<a, Context> <(b c), Context> ) if the expander needs
> to see the toplevel elements.) Because of this tupling, you cannot
> use
> ordinary car/cdr to decompose the input.
That is not accurate, but the misconception is understandable given
the language of R6RS.
For example, the explicit renaming system can be
implemented with linear complexity with ordinary s-expressions that
are
decomposable using car, cdr, etc. The same is true for syntax-case.
For example, my (Andre van Tonder's) syntax-case expander uses an
eager
algorithm based on a variant of explicit renaming and using ordinary
s-expressions decomposable using car/cdr. Thus, syntax-case can be
viewed as a convenience layer on top of explicit renaming.
Explicit renaming is really quite simple. See
http://groups.google.com/group/comp...4817ec9ffcccf39
I was disappointed that the R6RS editors decided to describe the lazy
algorithm in the R6RS standard, when a simple declarative description
of
hygiene/referential transparency as in R5RS would have sufficed.
Instead they chose to describe, as part of the standard, a specific,
non-unique implementation strategy, in fact probably the most
complicated implementation strategy anyone has bothered to
come up with to date, virtually ensuring that hardly anyone will
understand the document. I don't follow it, and I'm an implementor!
| |
| Jens Axel Soegaard 2008-02-12, 7:25 pm |
| Kjetil S. Matheussen wrote:
>
> I wouldn't say that. My impression is that most scheme implementations,
> and all the large ones (anyone who doesn't?), support define-macro. I
> would actually also be surprised if hygenic macros is more used by
> schemeres than "the old way", although I don't have any data to back
> that up with. Anyway, I would never use a scheme where you couldn't
> write macros the old way.
Nowadays there are (depending on point of view) two different
types of define-macro forms. One type is define-macro implemented
on top of define-syntax - this type is generally found in
implementations that have "full" syntax-case integration.
The other one is found in implementations where syntax-case
is treated as an "add on" builds on a "native" define-macro.
The two types are for simple macros equivalent, but for macros
that require phase 2 [*] computations hell is likely to break
loose.
There are two things I look for, when I evaluate macro systems:
1) How easy is it to give error messages in terms of the
*original* user syntax?
2) Can the module system handle the macro system?
[I.e. how (if at all) is the phase problem solved?]
Most "native" define-macro systems handle these points on an
ad hoc basis.
[*]
http://pre.plt-scheme.org/docs/html...ion_Model.html#(part~20module-phase)
| |
| andreuri2000@yahoo.com 2008-02-12, 7:25 pm |
| On Feb 12, 4:46 am, Ken Tilton <kennytil...@optonline.net> wrote:
> I see the problem, they have this whole pattern-matching syntax-rules
> language moving from source input to output. I have done (and am
> supposed to be doing) enough Prolog to realize Scheme macro writers must
> be way smarter than me. But... why? No, not why are they smarter. Why do
> they have to be smarter? What was wrong with treating the source as an
> ordinary tree of data and just whinging it about with Scheme itself to
> produce the output?
Nothing is wrong with your suggestion. Here is a simple macro
that will work in one of the R6RS reference implementations.
(define-syntax swap!
(lambda (form)
(let ((a (cadr form))
(b (caddr form)))
#`(let ((temp #,a))
(set! #,a #,b)
(set! #,b temp))))))
It is almost identical to the corresponding defmacro, except that
you use #` and #, instead of ` and ,. Also, no gensym is necessary.
You can think, as a fisrt approximation, of #` as automatically
inserting the necessary gensyms for you.
Andre
| |
| Raffael Cavallaro 2008-02-12, 7:25 pm |
| On 2008-02-12 11:26:54 -0500, andreuri2000@yahoo.com said:
> Nothing is wrong with your suggestion. Here is a simple macro
> that will work in one of the R6RS reference implementations.
>
> (define-syntax swap!
> (lambda (form)
> (let ((a (cadr form))
> (b (caddr form)))
> #`(let ((temp #,a))
> (set! #,a #,b)
> (set! #,b temp))))))
>
> It is almost identical to the corresponding defmacro, except that
> you use #` and #, instead of ` and ,. Also, no gensym is necessary.
> You can think, as a fisrt approximation, of #` as automatically
> inserting the necessary gensyms for you.
Not that I doubt the power of your system Andre, but could you show us
a simple example of intentional capture inside a #` form?
| |
| Maciej Katafiasz 2008-02-12, 7:25 pm |
| Den Tue, 12 Feb 2008 04:46:15 -0500 skrev Ken Tilton:
> While trying to explain my irrational prejudice against Scheme macros I
> got curious as to why I hated them, so I googled up this:
>
> http://www.xs4all.nl/~hipster/lib/s.../define-syntax-
primer.txt
>
> Boys and girls, that is almost eleven thousand words long!!!!!
Woah there, back in my day, a three-article technical *series* was
absolutely, unquestionably, your editor is really good at using Delete
thank you very much limited to 6K words (including intros, conclusions
and recaps). So that's two complete series there.
> Please restrict yourself to one or more of these
> answers:
>
> 4. Other _____________________
They had to write articles with strict wordcount limits before and decide
that this time XXXX it, *they* will decide when to stop.
Cheers,
Maciej
| |
| michele.simionato@gmail.com 2008-02-12, 7:25 pm |
| I have been skeptical about Scheme macros for a long time,
thinking that syntax-rules is too simple and syntax-case too complex,
and I have been using define-macro instead. However, in the last
couple of months I have somewhat changed my mind. I figured out that
the biggest issue I had with syntax-case was notational. If you are
willing to dress it with a minor amount of sugar, syntax-case becomes
pretty easy to use and quite readable too. Here is the swap! example:
(define-syntax-case swap! ()
((swap! x y) (let ((t x)) (set! x y) (set! y t))))
define-syntax-case is just sugar around syntax-case:
;; tested on Ikarus Scheme
(define-syntax define-syntax-case
(syntax-rules ()
((_ name (literal ...)
((_ arg ...) templ) ...)
(define-syntax name
(lambda (x)
(syntax-case x (<expand> literal ...)
((ctx <expand> arg ...) #`'templ) ...
((ctx arg ...) #`templ) ...))))
((_ name (literal ...)
((_ arg ...) fender templ) ...)
(define-syntax name
(lambda (x)
(syntax-case x (<expand> literal ...)
((ctx <expand> arg ...) fender #`'templ) ...
((ctx arg ...) fender #`templ) ...))))
))
Notice that I also added an <expand> feature to see the expansion
of the macro:
(swap! <expand> x y) ;=> (let ((t x)) (set! x y) (set! y t))
Michele Simionato
| |
| andreuri2000@yahoo.com 2008-02-12, 7:25 pm |
| On Feb 12, 12:31 pm, Raffael Cavallaro <raffaelcavallaro@pas-d'espam-
s'il-vous-plait-mac.com> wrote:
> Not that I doubt the power of your system Andre, but could you show us
> a simple example of intentional capture inside a #` form?
(define-syntax if-it
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
#`(let ((,it ,(cadr form)))
(if #,it
#,(caddr form)
#,(cadddr form))))))
DATUM->SYNTAX overrides hygiene by creating the identifier IT as if
it
appeared in the source at the position of (CAR FORM), i.e., the IF-IT
at the usage site of the macro. You may think of this, very roughly,
as a standard way of making an identifier that is not affected by the
automatic gensym that would otherwise be applied by #`.
This is slightly longer than the defmacro way, but macros that
capture
are usually a minority. Most defmacros can be trivially converted
to Scheme hygienic macros (in this R6RS reference implementation)
by omitting gensyms and putting #s in front of the `s,'s, and ,s
and would therefore be shortened rather than lengthened.
Unfortunately this is not true for all implementations of R6RS Scheme
macros. Some unfortunately do not represent code as s-expressions
and rely on pattern matching to decompose their input.
While there are reasons for their design choice, I think it very
unfortunately obscures the fact that pattern matching is absolutely
orthogonal to - and unnecessary for - the hygiene mechanism. As a
result,
people like the OP get the impression that hygiene is more
complicated
than it really is, and cannot entirely be blamed for giving
up in disgust.
Andre
| |
| andreuri2000@yahoo.com 2008-02-12, 7:25 pm |
| On Feb 12, 2:07 pm, andreuri2...@yahoo.com wrote:
> (define-syntax if-it
> (lambda (form)
> (let ((it (datum->syntax (car form) 'it)))
> #`(let ((,it ,(cadr form)))
> (if #,it
> #,(caddr form)
> #,(cadddr form))))))
Sorry. I meant:
(define-syntax if-it
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
#`(let ((#,it #,(cadr form)))
(if #,it
#,(caddr form)
#,(cadddr form))))))
Andre
| |
| Abdulaziz Ghuloum 2008-02-12, 7:26 pm |
| michele.simionato@gmail.com wrote:
> (define-syntax define-syntax-case
> (syntax-rules ()
> ((_ name (literal ...)
> ((_ arg ...) templ) ...)
> (define-syntax name
> (lambda (x)
> (syntax-case x (<expand> literal ...)
> ((ctx <expand> arg ...) #`'templ) ...
> ((ctx arg ...) #`templ) ...))))
> ((_ name (literal ...)
> ((_ arg ...) fender templ) ...)
> (define-syntax name
> (lambda (x)
> (syntax-case x (<expand> literal ...)
> ((ctx <expand> arg ...) fender #`'templ) ...
> ((ctx arg ...) fender #`templ) ...))))
> ))
I wonder how this would look using s-expression manipulation
(cars/cdrs). Andre, since you seem to be the expert on this,
do you want to give it a shot?
| |
| Eli Barzilay 2008-02-12, 7:26 pm |
| "Kjetil S. Matheussen" <k.s.matheussen@notam02.no> writes:
> I wouldn't say that. My impression is that most scheme
> implementations, and all the large ones (anyone who doesn't?),
> support define-macro. I would actually also be surprised if hygenic
> macros is more used by schemeres than "the old way", although I
> don't have any data to back that up with. [...]
Out of curiosity, I inspected the PLT collects tree. Here's a
summary:
* about 400K lines of Scheme code
* 12 occurrences of `define-macro'; 5 are in the define-macro library
source, 3 in comments, 4 are in misc meta-code (keyword colors,
indentation, etc), that leaves 0 uses in real code.
* 1438 occurrences of `define-syntax' (and `...-syntaxes')
* 1360 of `syntax-case'
* 468 of `syntax-rules'
It's interesting to compare this to version 103 (which is before PLT
switched to `syntax-case' being native):
* About 150K lines
* 125 `define-macro'
* 7 `define-syntax', 1 `syntax-rules', 1 `syntax-case' none is
actually used
And here's an interesting result: if the switch to the new system
would mean changing all `define-macro's to `define-syntax', and
assuming the same "macro use density" per line of code, then you'd
expect to see about 300 syntax definitions. So the new macro system is
robust enough that it is used about 4.5 times more often than
define-macro.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!
| |
| Eli Barzilay 2008-02-12, 7:26 pm |
| Ken Tilton <kennytilton@optonline.net> writes:
> While trying to explain my irrational prejudice against Scheme
> macros I got curious as to why I hated them, so I googled up this:
>
> http://www.xs4all.nl/~hipster/lib/s...ntax-primer.txt
>
> Boys and girls, that is almost eleven thousand words long!!!!!
Note the name of the document -- it describes `syntax-rules' and how
to do with it things that seem impossible from a very restricted
syntax rewriting system. Using *just* that is on the same masochistic
level as using *just* r5rs for real programming.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!
| |
| Abdulaziz Ghuloum 2008-02-12, 7:26 pm |
| Eli Barzilay wrote:
> So the new macro system is robust enough that it is
> used about 4.5 times more often than define-macro.
Or, it may be that you guys are just macro noobs. You
can't handle the power of lisp macros, and you want
this thing called "hygiene" to protect you from the so
called "unintended capture" straw-man that you throw
around at every occasion.
The real programmer does not need no stinkin' hygiene.
| |
| andreuri2000@yahoo.com 2008-02-12, 7:26 pm |
| On Feb 12, 3:24 pm, Abdulaziz Ghuloum <aghul...@cee.ess.indiana.edu>
wrote:
> michele.simion...@gmail.com wrote:
>
> I wonder how this would look using s-expression manipulation
> (cars/cdrs). Andre, since you seem to be the expert on this,
> do you want to give it a shot?
I'll leave that to the Lispniks, who have more practice
with this kind of thing. It might even occur to some of
them that pattern matching, despite being orthogonal to and
unnecessary for hygiene, is a quite useful thing to have.
| |
| Shiro Kawai 2008-02-12, 7:26 pm |
| andreuri2...@yahoo.com wrote:
> That is not accurate, but the misconception is understandable given
> the language of R6RS.
>
> For example, the explicit renaming system can be
> implemented with linear complexity with ordinary s-expressions that
> are
> decomposable using car, cdr, etc. The same is true for syntax-case.
> For example, my (Andre van Tonder's) syntax-case expander uses an
> eager
> algorithm based on a variant of explicit renaming and using ordinary
> s-expressions decomposable using car/cdr. Thus, syntax-case can be
> viewed as a convenience layer on top of explicit renaming.
> Explicit renaming is really quite simple. Seehttp://groups.google.com/group/comp.lang.scheme/msg/a4817ec9ffcccf39
Aha! I've seen the explicit renaming expander but have never looked
at
it closely. Now I read the articles and found it made a lot of sense,
and it feels more Scheme-way to me. I'll give it a shot. Thanks!
| |
| michele.simionato@gmail.com 2008-02-13, 4:30 am |
| On Feb 13, 12:03 am, andreuri2...@yahoo.com wrote:
> I'll leave that to the Lispniks, who have more practice
> with this kind of thing. It might even occur to some of
> them that pattern matching, despite being orthogonal to and
> unnecessary for hygiene, is a quite useful thing to have.
Actually one of the reasons why I changed my mind about Scheme
macros is that I realized the benefits of pattern matching
when writing complex macros. BTW, I just noticed that I have posted
the wrong version of define-syntax-case, the correct one is
(define-syntax define-syntax-case
(syntax-rules ()
((_ name (literal ...)
((ctx arg ...) templ) ...)
(define-syntax name
(lambda (x)
(syntax-case x (<expand> literal ...)
((ctx <expand> arg ...) #`'templ) ...
((ctx arg ...) #`templ) ...))))
((_ name (literal ...)
((ctx arg ...) fender templ) ...)
(define-syntax name
(lambda (x)
(syntax-case x (<expand> literal ...)
((ctx <expand> arg ...) fender #`'templ) ...
((ctx arg ...) fender #`templ) ...))))
))
| |
| Alex Shinn 2008-02-13, 8:13 am |
| >>>>> "Ken" == Ken Tilton <kennytilton@optonline.net> writes:
[ ... why syntax-rules? ... ]
Ken> 4. Other _____________________
I won't presume as to the R5RS editors original reasons, but
I suspect it has to do with the fact that SYNTAX-RULES was
and remains the simplest and most natural macro system for
describing all of the derived syntax in R5RS itself.
There are plenty of other cases where SYNTAX-RULES clearly
wins. Just consider the confusion that a newbie recently
ran into trying to use DEFINE-MACRO:
http://list.cs.brown.edu/pipermail/...ary/022777.html
.... and how SYNTAX-RULES saves the day:
http://list.cs.brown.edu/pipermail/...ary/022782.html
--
Alex
| |
| Eli Barzilay 2008-02-13, 7:15 pm |
| Abdulaziz Ghuloum <aghuloum@cee.ess.indiana.edu> writes:
> Eli Barzilay wrote:
>
>
> Or, it may be that you guys are just macro noobs.
No.
> You can't handle the power of lisp macros, and you want this thing
> called "hygiene" to protect you from the so called "unintended
> capture" straw-man that you throw around at every occasion.
>
> The real programmer does not need no stinkin' hygiene.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://www.barzilay.org/ Maze is Life!
| |
| Pascal Costanza 2008-02-13, 7:15 pm |
| andreuri2000@yahoo.com wrote:
> On Feb 12, 12:31 pm, Raffael Cavallaro <raffaelcavallaro@pas-d'espam-
> s'il-vous-plait-mac.com> wrote:
>
>
> (define-syntax if-it
> (lambda (form)
> (let ((it (datum->syntax (car form) 'it)))
> #`(let ((,it ,(cadr form)))
> (if #,it
> #,(caddr form)
> #,(cadddr form))))))
>
> DATUM->SYNTAX overrides hygiene by creating the identifier IT as if
> it appeared in the source at the position of (CAR FORM), i.e., the
> IF-IT at the usage site of the macro. You may think of this, very
> roughly, as a standard way of making an identifier that is not
> affected by the automatic gensym that would otherwise be applied by
> #`.
This sounds like I have to say twice where the identifier is inserted.
That's weird. Why is that the case?
> This is slightly longer than the defmacro way, but macros that
> capture are usually a minority.
Says who?
> Some unfortunately do not represent code as s-expressions and rely on
> pattern matching to decompose their input. While there are reasons
> for their design choice, I think it very unfortunately obscures the
> fact that pattern matching is absolutely orthogonal to - and
> unnecessary for - the hygiene mechanism. As a result, people like the
> OP get the impression that hygiene is more complicated than it really
> is, and cannot entirely be blamed for giving up in disgust.
Yes, you're macro system looks more attractive than syntax-case to me. I
understand that syntax-rules can be neat when it's all you need, but for
more complex stuff, s-expressions are simply better (IMHO, and at all
that, of course).
Does your system also support the kinds of macros that are mentioned for
example in
http://www.lispworks.com/documentat...es/iss066_w.htm ?
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Kaz Kylheku 2008-02-13, 7:15 pm |
| On Feb 13, 5:39=A0am, Alex Shinn <alexsh...@gmail.com> wrote:
> There are plenty of other cases where SYNTAX-RULES clearly
> wins. =A0Just consider the confusion that a newbie recently
> ran into trying to use DEFINE-MACRO:
>
> =A0http://list.cs.brown.edu/pipermail/plt-scheme/2008-February/022777.html=
>
> ... and how SYNTAX-RULES saves the day:
>
> =A0http://list.cs.brown.edu/pipermail/plt-scheme/2008-February/022782.html=
I can hardly make heads or tails out of the nested defspel. I need to
see the nested backquotes to see what is the meta-level and what is
meta-meta, otherwise my eyes glaze over. I can't see what part of the
template is literal, and what part is spliced in, because nothing is
denoting that. And look, even though things are quoted, they are still
substituted. You have expressions like (eq? 'object 'obj), but this is
really like `(eq? (quote ,object) (quote ,obj)). It looks like these
are simple quoted literal symbols, but no, there is still evaluation
taking place. Confusing! Suppose you're a sloppy Frenchman and make a
typo and write (eq? 'objet 'obj). The 'objet is now literal, because
objet isn't a macro parameter. That's evil!
This basically brings us back to the stone age of C language macros:
#define STR(X) #X
#define SPEL(OBJECT, SUBJECT) foo(SUBJECT, STR(OBJET))
Look at this, here, too, there is no ugly backquote to confuse
newbies! C preprocessor saves the day! Of course, when you call
SPEL(X, Y) you end up with foo(X, "OBJET") instead of foo(X, "Y").
Oops!
| |
| William D Clinger 2008-02-13, 7:15 pm |
| When you get right down to it, the real reason we
added syntax-case to Scheme was #' envy.
Will
| |
| Raffael Cavallaro 2008-02-13, 7:15 pm |
| On 2008-02-12 14:37:54 -0500, andreuri2000@yahoo.com said:
> Sorry. I meant:
>
> (define-syntax if-it
> (lambda (form)
> (let ((it (datum->syntax (car form) 'it)))
> #`(let ((#,it #,(cadr form)))
> (if #,it
> #,(caddr form)
> #,(cadddr form))))))
>
> Andre
Thanks. I can get this to work using:
(ex:repl '((import (rnrs)) ...
but is there some way to get an *interactive* repl in one of the
compatible schemes? I've tried the existing
r6rs-expander-vantonder.plt, but it's kind annoying to use. For
example, if I enter your definition above:
R5.97RS> (define-syntax if-it
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
#`(let ((#,it #,(cadr form)))
(if #,it
#,(caddr form)
#,(cadddr form))))))
Syntax violation: invalid reference
Attempt to use binding of lambda in library () at invalid level 1.
Binding is only available at levels: 0
Form: lambda
Trace:
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
(quasisyntax
(let (((unsyntax it) (unsyntax (cadr form))))
(if (unsyntax it) (unsyntax (caddr form)) (unsyntax (cadddr form)))))))
syntax-violation: Integrate with host error handling here
More or less defeats the purpose of a repl if I have to define a
library for such simple things - unless I'm misunderstanding the error
altogether.
| |
| William D Clinger 2008-02-13, 10:13 pm |
| Raffael Cavallaro wrote:
> Thanks. I can get this to work using:
>
> (ex:repl '((import (rnrs)) ...
>
> but is there some way to get an *interactive* repl in one of the
> compatible schemes?
Larceny's ERR5RS mode uses Andre's interactive REPL as
its native REPL:
% larceny -err5rs
Larceny v0.961 "Fluoridation" (Jan 2 2008 04:27:56,
precise:SunOS5:split)
ERR5RS mode (no libraries have been imported)
> (import (rnrs))
Autoloading (rnrs)
Autoloading (rnrs enums)
Autoloading (rnrs lists)
Autoloading (rnrs syntax-case)
Autoloading (rnrs hashtables)
Autoloading (rnrs arithmetic bitwise)
Autoloading (rnrs programs)
Autoloading (rnrs files)
Autoloading (rnrs io ports)
Autoloading (larceny deprecated)
Autoloading (rnrs conditions)
Autoloading (rnrs exceptions)
Autoloading (rnrs records syntactic)
Autoloading (err5rs records procedural)
Autoloading (rnrs records procedural)
Autoloading (rnrs control)
Autoloading (rnrs sorting)
Autoloading (rnrs bytevectors)
Autoloading (rnrs unicode)
> (define-syntax if-it
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
#`(let ((#,it #,(cadr form)))
(if #,it
#,(caddr form)
#,(cadddr form))))))
>
Will
| |
|
| "William D Clinger" <cesura17@yahoo.com> wrote in message
news:5c7a0076-f785-4011-8416-f40c525fde2a@e25g2000prg.googlegroups.com...
> Raffael Cavallaro wrote:
>
> Larceny's ERR5RS mode uses Andre's interactive REPL as
> its native REPL:
>
> % larceny -err5rs
> Larceny v0.961 "Fluoridation" (Jan 2 2008 04:27:56,
> precise:SunOS5:split)
> ERR5RS mode (no libraries have been imported)
>
> Autoloading (rnrs)
> Autoloading (rnrs enums)
> Autoloading (rnrs lists)
> Autoloading (rnrs syntax-case)
> Autoloading (rnrs hashtables)
> Autoloading (rnrs arithmetic bitwise)
> Autoloading (rnrs programs)
> Autoloading (rnrs files)
> Autoloading (rnrs io ports)
> Autoloading (larceny deprecated)
> Autoloading (rnrs conditions)
> Autoloading (rnrs exceptions)
> Autoloading (rnrs records syntactic)
> Autoloading (err5rs records procedural)
> Autoloading (rnrs records procedural)
> Autoloading (rnrs control)
> Autoloading (rnrs sorting)
> Autoloading (rnrs bytevectors)
> Autoloading (rnrs unicode)
>
> (lambda (form)
> (let ((it (datum->syntax (car form) 'it)))
> #`(let ((#,it #,(cadr form)))
> (if #,it
> #,(caddr form)
> #,(cadddr form))))))
>
>
> Will
That's nice. Now show an usage example. Every Scheme I tried choked on on
the application of that macro.
| |
| Abdulaziz Ghuloum 2008-02-14, 4:31 am |
| llama wrote:
> Now show an usage example. Every Scheme I tried choked on
> on the application of that macro.
Andre already told you that this macro that he's written uses
an extension that's not supported by any any implementation
except larceny (by virtue of using his system). Fortunate or
unfortunate, there *is* a portable way (as far as R6RS goes)
of writing this macro as shown below (it might run on other
systems that I have not tested). The portable version also
happens to be more robust--it gives a better error message if
the input is malformed instead of giving the ``error in
caddr'' sort of error that you'd get otherwise.
Pick the version that better suits your needs.
Aziz,,,
$ ikarus
Ikarus Scheme version 0.0.3+ (revision 1384, build 2008-02-13)
Copyright (c) 2006-2008 Abdulaziz Ghuloum
> (define-syntax if-it
(lambda (stx)
(syntax-case stx ()
((ctxt test conseq altern)
(let ((it (datum->syntax #'ctxt 'it)))
#`(let ((#,it test))
(if #,it conseq altern)))))))
> (if-it 17 it 18)
17
$ larceny -err5rs
Larceny v0.961 "Fluoridation" (Jan 2 2008 04:30:22, precise:Posix
Unix:unified)
larceny.heap, built on Wed Jan 2 04:41:29 EST 2008
ERR5RS mode (no libraries have been imported)
> (import (rnrs))
Autoloading ...
> (define-syntax if-it
(lambda (stx)
(syntax-case stx ()
((ctxt test conseq altern)
(let ((it (datum->syntax #'ctxt 'it)))
#`(let ((#,it test))
(if #,it conseq altern)))))))
> (if-it 17 it 18)
17
$ mzscheme
Welcome to MzScheme v3.99.0.10 [3m], Copyright (c) 2004-2008 PLT Scheme Inc.
> (define-syntax if-it
(lambda (stx)
(syntax-case stx ()
((ctxt test conseq altern)
(let ((it (datum->syntax #'ctxt 'it)))
#`(let ((#,it test))
(if #,it conseq altern)))))))
> (if-it 17 it 18)
17
$ petite
Petite Chez Scheme Version 7.4
Copyright (c) 1985-2007 Cadence Research Systems
> (define-syntax if-it
(lambda (stx)
(syntax-case stx ()
((ctxt test conseq altern)
(let ((it (datum->syntax #'ctxt 'it)))
#`(let ((#,it test))
(if #,it conseq altern)))))))
> (if-it 17 it 18)
17
| |
| William D Clinger 2008-02-14, 4:31 am |
| llama wrote:
> That's nice. Now show an usage example. Every Scheme I tried choked on on
> the application of that macro.
It works in Larceny. See below.
Will
--------
% larceny -err5rs
Larceny v0.961 "Fluoridation" (Jan 19 2008 12:07:22,
precise:SunOS5:split)
ERR5RS mode (no libraries have been imported)
> (import (rnrs))
Autoloading (rnrs)
Autoloading (rnrs enums)
Autoloading (rnrs lists)
Autoloading (rnrs syntax-case)
Autoloading (rnrs hashtables)
Autoloading (rnrs arithmetic bitwise)
Autoloading (rnrs programs)
Autoloading (rnrs files)
Autoloading (rnrs io ports)
Autoloading (larceny deprecated)
Autoloading (rnrs conditions)
Autoloading (rnrs exceptions)
Autoloading (rnrs records syntactic)
Autoloading (err5rs records procedural)
Autoloading (rnrs records procedural)
Autoloading (rnrs control)
Autoloading (rnrs sorting)
Autoloading (rnrs bytevectors)
Autoloading (rnrs unicode)
> (define-syntax if-it
(lambda (form)
(let ((it (datum->syntax (car form) 'it)))
#`(let ((#,it #,(cadr form)))
(if #,it
#,(caddr form)
#,(cadddr form))))))
> (if-it #t 'then 'else)
then
> (if-it #f 'then 'else)
else
> (if-it #t it 'else)
#t
> ;; Now for a more familiar use of intentional capture.
(define-syntax myloop
(lambda (form)
(let ((break (datum->syntax (car form) 'break)))
#`(call/cc
(lambda (k)
(let ((#,break k))
(do ()
(#f)
(begin #,@(cdr form)))))))))
> (let ((x '(3 2 1 0 -1 -2 -3 -4)))
(myloop (if (zero? (car x))
(break (cdr x))
(set! x (cdr x)))))
(-1 -2 -3 -4)
| |
| Raffael Cavallaro 2008-02-14, 4:31 am |
| On 2008-02-13 22:12:38 -0500, William D Clinger <cesura17@yahoo.com> said:
[color=darkred]
> Larceny's ERR5RS mode uses Andre's interactive REPL as
> its native REPL:
>
> % larceny -err5rs
> Larceny v0.961 "Fluoridation" (Jan 2 2008 04:27:56,
> precise:SunOS5:split)
> ERR5RS mode (no libraries have been imported)
>
Ah, I remember now reading something about this on the larceny mailing
list. Thanks - I've just written, compiled, loaded, imported and run my
first err5rs/r6rs library - works nicely.
| |
| bunny351@yoho-gmail.com 2008-02-14, 4:31 am |
| On Feb 12, 9:45 pm, Eli Barzilay <e...@barzilay.org> wrote:
> "Kjetil S. Matheussen" <k.s.matheus...@notam02.no> writes:
>
>
> Out of curiosity, I inspected the PLT collects tree. Here's a
> summary:
>
> * about 400K lines of Scheme code
>
> * 12 occurrences of `define-macro'; 5 are in the define-macro library
> source, 3 in comments, 4 are in misc meta-code (keyword colors,
> indentation, etc), that leaves 0 uses in real code.
>
> * 1438 occurrences of `define-syntax' (and `...-syntaxes')
>
> * 1360 of `syntax-case'
>
> * 468 of `syntax-rules'
>
As a comparison, here a grep through the chicken code repository
(including tagged versions):
define-macro: 9235
define-syntax: 5504
syntax-rules: 5249
cheers,
felix
| |
| William D Clinger 2008-02-14, 8:13 am |
| Tabulating data collected by Eli Barzilay and
Felix Winkelmann:
PLT Chicken Larceny
define-macro 12 9235 67
define-syntax 1438 5504 1806
syntax-rules 468 5249 1703
syntax-case 1360 ? 271
For Larceny, most of the macros are defined in
libraries and benchmarks written by people who
are not themselves developers of Larceny.
Will
| |
| Pascal Costanza 2008-02-14, 8:13 am |
| Alex Shinn wrote:
>
> [ ... why syntax-rules? ... ]
>
> Ken> 4. Other _____________________
>
> I won't presume as to the R5RS editors original reasons, but
> I suspect it has to do with the fact that SYNTAX-RULES was
> and remains the simplest and most natural macro system for
> describing all of the derived syntax in R5RS itself.
I don't think that syntax rules. ;)
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
|
| "Abdulaziz Ghuloum" <aghuloum@cee.ess.indiana.edu> wrote in message
news:fp0m97$iki$1@aioe.org...
> llama wrote:
>
>
> Andre already told you that this macro that he's written uses
> an extension that's not supported by any any implementation
> except larceny (by virtue of using his system).
Thanks, I must have missed that part.
| |
| michele.simionato@gmail.com 2008-02-15, 4:31 am |
| On Feb 15, 1:30 am, Abdulaziz Ghuloum <aghul...@cee.ess.indiana.edu>
wrote:list a b)))
>
> $ ikarus --r6rs-script test.ss
> Unhandled exception:
> Condition components:
> 1. &who: let
> 2. &message: "not an identifier"
> 3. &syntax:
> form: (let ((17 b)) (list a b))
> subform: 17
> 4. &trace: #<syntax (let ((17 b)) (list a b))>
> 5. &trace: #<syntax (foo 17 b (list a b))>
> 6. &trace: #<syntax (bar b 17 (list a b)) [byte 200 of test.ss]>
Why I don't get the traceback? My output stops at point 3. Are you
referring to a development version of Ikarus, or there is a debug
flag I should set somewhere to see the traceback? I am using
Ikarus 0.0.3.
Michele Simionato
| |
| Kaz Kylheku 2008-02-15, 4:31 am |
| On Feb 14, 4:30=A0pm, Abdulaziz Ghuloum <aghul...@cee.ess.indiana.edu>
wrote:
> $ ikarus --r6rs-script test.ss
> Unhandled exception:
> =A0 Condition components:
> =A0 =A0 1. &who: let
> =A0 =A0 2. &message: "not an identifier"
> =A0 =A0 3. &syntax:
> =A0 =A0 =A0 =A0 form: (let ((17 b)) (list a b))
> =A0 =A0 =A0 =A0 subform: 17
> =A0 =A0 4. &trace: #<syntax (let ((17 b)) (list a b))>
> =A0 =A0 5. &trace: #<syntax (foo 17 b (list a b))>
> =A0 =A0 6. &trace: #<syntax (bar b 17 (list a b)) [byte 200 of test.ss]>
>
> As you can see, it shows the original file name and position in
> terms of the user's original source code. =A0
S-expressions read from a file (and all their constituent
expressions), can be associated this information in a global hash
table. Given some erroneous form, the macro can see if there is
associated information with that form.
In principle, the origin of every literal object (or at least that of
every boxed one) that was read from some source file can be
pinpointed.
> Some may think that this advantage does not outweigh dropping
> the raw s-expression form.
It doesn't seem to /technically/ require such dropping.
>=A0But others value things differently.
E.g. eating the cake and having it too.
| |
| Abdulaziz Ghuloum 2008-02-15, 4:31 am |
| michele.simionato@gmail.com wrote:
> Why I don't get the traceback? My output stops at point 3. Are you
> referring to a development version of Ikarus, or there is a debug
> flag I should set somewhere to see the traceback? I am using
> Ikarus 0.0.3.
I just added that yesterday :-) So, you can get it from the repo:
$ bzr checkout --lightweight http://www.cs.indiana.edu/~aghuloum/ikarus.dev
| |
| Abdulaziz Ghuloum 2008-02-15, 4:31 am |
| Kaz Kylheku wrote:
>
> It doesn't seem to /technically/ require such dropping.
Neither did I say it was, nor am I going to attempt to
prove otherwise. Actually, there may already exist some
system that somehow manages to pull it off, but to my
limited knowledge, I have not seen it done. Moreover,
adding the trace to my expander took no more than 45
minutes of doing minor edits to one file. Additionally,
the trace information (which is associated with the syntax
objects themselves) is not interned anywhere, and works
across files and will just work even with separately
compiled libraries.
All I show here is the advantage that *I* got from having
special syntax objects over having to use s-expressions.
Mileages vary.
Aziz,,,
| |
| Raffael Cavallaro 2008-02-15, 4:31 am |
| On 2008-02-14 16:40:06 -0500, William D Clinger <cesura17@yahoo.com> said:
> Results:
>
> PLT Chicken Larceny
> define-macro 66 4234 67
> define-syntax 1908 3583 1807
> syntax-rules 651 3297 1704
> syntax-case 1675 1110 272
>
> Each of the three columns shows a different pattern, so
> generalizations based on a single programmer's experience
> or on the code written for any one implementation should
> be regarded with skepticism.
I wonder what the column for Gambit would look like - syntax-case and
define-syntax are not loaded by default in Gambit so I would suspect
they don't figure heavily in Gambit's own source.
| |
| andreuri2000@yahoo.com 2008-02-15, 7:18 pm |
| On Feb 13, 3:47 pm, Kaz Kylheku <kkylh...@gmail.com> wrote:
> I can hardly make heads or tails out of the nested defspel. I need to
> see the nested backquotes to see what is the meta-level and what is
> meta-meta, otherwise my eyes glaze over.
This may be a question of familiarity. I tend to glaze
over when confronted by the nested quasiquotations that tend to
appear
in macro-generating macros. Here is a snippet from
one of my own old define-macros
`(define-macro (,name ,k ,@args)
`(,,k ,',supers ,',labels ,@,args)))))
and a little quiz:
(let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x)) => ??
Define-syntax templates are easier for me in these situations.
> I can't see what part of the
> template is literal, and what part is spliced in, because nothing is
> denoting that.
That is certainly a valid complaint. It could be mitigated somewhat
by
a notational convention like
(define-syntax swap
(syntax-rules ()
((_ x y) (LET ((TEMP x))
(SET! x y)
(SET! y TEMP)))))
or by text colorization in a sufficiently smart editor. But this is
not typically something that seems to trip up Scheme macro writers
too much.
| |
| Joel J. Adamson 2008-02-15, 7:18 pm |
| Raffael Cavallaro <raffaelcavallaro@pas-d'espam-s'il-vous-plait-mac.com>
writes:
> On 2008-02-14 16:40:06 -0500, William D Clinger <cesura17@yahoo.com> said:
>
>
> I wonder what the column for Gambit would look like - syntax-case and
> define-syntax are not loaded by default in Gambit so I would suspect
> they don't figure heavily in Gambit's own source.
We were simultaneously having a similar discussion on gambit-list: A
quick check of the examples turned up (obviously) no uses of syntax-case
style macros. This discussion started with my mistaken presumption that
I needed to use syntax-case macros and dealing with the problems created
by loading the syntax-case library packaged with Gambit. The response
from a few readers was that Gambit favors define-macro, and indeed
that's all that I find in the examples and I, personally find their
construction more intuitive than syntax-rules templates.
Joel
--
Joel J. Adamson
Biostatistician
Pediatric Psychopharmacology Research Unit
Massachusetts General Hospital
Boston, MA 02114
(617) 643-1432
(303) 880-3109
| |
|
| <andreuri2000@yahoo.com> wrote in message
news:c1f34d0e-a312-46e7-a0cc-af056de5c881@u10g2000prn.googlegroups.com...
> and a little quiz:
>
> (let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x)) => ??
>
Are the last 2 cases (,,@x and ,@,@x) even valid?
| |
| andreuri2000@yahoo.com 2008-02-15, 7:18 pm |
| On Feb 15, 10:56 am, "llama" <l...@winamp.com> wrote:
> <andreuri2...@yahoo.com> wrote in message
>
>
> Are the last 2 cases (,,@x and ,@,@x) even valid?
They are valid in R6RS Scheme. I'm not sure about
Common Lisp.
| |
|
| <andreuri2000@yahoo.com> wrote in message
news:789c3a40-a41f-46e4-b61f-b8042136d25a@s8g2000prg.googlegroups.com...
> On Feb 15, 10:56 am, "llama" <l...@winamp.com> wrote:
>
> They are valid in R6RS Scheme. I'm not sure about
> Common Lisp.
Sorry, I didnt mean syntactically valid, I meant valid in the sense of the
computation. So will it produce a result?
What is the behaviour around unquoting/unquotesplicing a spliced list?
I prefer nested quasiquoting with a bit of space between them :)
| |
| Kaz Kylheku 2008-02-15, 7:18 pm |
| On Feb 15, 7:18=A0am, andreuri2...@yahoo.com wrote:
> On Feb 13, 3:47 pm, Kaz Kylheku <kkylh...@gmail.com> wrote:
>
>
> This may be a question of familiarity. I tend to glaze
> over when confronted by the nested quasiquotations that tend to
> appear in macro-generating macros.
>
>=A0Here is a snippet from
> one of my own old define-macros
>
> =A0 =A0 =A0 =A0`(define-macro (,name ,k ,@args)
> =A0 =A0 =A0 =A0 =A0`(,,k ,',supers ,',labels ,@,args)))))
Even in the absence of enough context, I know what's going on here. I
don't have the bindings of name, k, supers and labels are, but I can
see exactly how they are being integrated into the structure. I can
tell that define-macro is a literal symbol in the template, regardless
of any binding.
> and a little quiz:
>
> =A0 (let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x)) =A0 =3D> ??
The result is the equivalent of this object (itself, not its value):
`(,(a b c) ,@(a b c) ,a ,b ,c ,@a ,@b ,@c)
The ,x's are straightforward; they are simply replaced by (a b c). If
we look at in in the Scheme way, the splicing ,@x's under under an
unquote or unquote-splicing operator effectively cause it to have
multiple arguments. Both operators apply the same evaluation each of
their arguments, and then splice in the resulting values, resulting in
a distributive law: (unquote a b c) means the same thing as (unquote
a) (unquote b) (unquote c), and by means of this reduction to single-
argument forms, we can turn it back into the comma notation ,a ,b ,c.
I simply remember the distributive law in terms of the shorthand
operators themselves. ,,@x means that x is spliced in, and the inner
comma distributes into the elements of the splice.
Nested backquote is indeed something you have to ``get'', and that can
take time.
| |
| andreuri2000@yahoo.com 2008-02-15, 7:18 pm |
| Let me point out that the simplicity of defmacro is
sometimes exaggerated when compared to define-syntax.
For example, the following let-macro
(define-syntax let
(syntax-rules ()
((_ ((x v) ...) b1 b2 ...)
((lambda (x ...) b1 b2 ...) v ...))))
is /not/ the same as the superficially simple
(define-macro (let bindings . body)
`((lambda ,(map car bindings) ,@body) ,@(map cdr bindings)))
but rather expresses the following:
(define-macro (let . rest)
(unless
(and (list? rest)
(>= (length rest) 2))
(syntax-error 'let
"Does not match (((x v) ...) b1 b2 ...)" rest))
(unless
(and (list? (car rest))
(andmap (lambda (binding)
(= (length binding) 2))
(car rest)))
(syntax-error 'let
"Does not match ((x v) ...)" (car rest)))
`((lambda ,(map car (car rest)) ,@(cdr rest))
,@(map cdr (car rest))))
Andre
| |
| Abdulaziz Ghuloum 2008-02-15, 7:18 pm |
| andreuri2000@yahoo.com wrote:
> (and (list? (car rest))
> (andmap (lambda (binding)
(and (list? binding)
> (= (length binding) 2))
> (car rest)))
> (syntax-error 'let
> "Does not match ((x v) ...)" (car rest)))
> `((lambda ,(map car (car rest)) ,@(cdr rest))
> ,@(map cdr (car rest))))
>
> Andre
| |
| Kaz Kylheku 2008-02-15, 7:18 pm |
| On Feb 15, 9:02=A0am, andreuri2...@yahoo.com wrote:
> On Feb 15, 10:56 am, "llama" <l...@winamp.com> wrote:
>
>
>
>
> They are valid in R6RS Scheme.
By golly, R5RS doesn't seem define this, only R5R6, which allows
multiple arguments to unquote and unquote-splicing, and contains
nested backquote examples with this type of syntax.
Could it be really be that Scheme had broken backquotes until
recently? I'm rubbing my eyes.
>=A0I'm not sure about Common Lisp.
The distributed-law behavior pops out of the axioms of the semantic
description of backquote expansion in the standard.
Essentially, `(x1 x2 x3 ... . atom) is defined as being equivalent to
(append [x1] [x2] ... [xn]), where if [xi] looks like ,form it is
interpreted as (list form) and consequently ,,@form means
(list ,@form). I.e. the unquote is defined in terms of LIST, which
handles additional arguments similarly to how the Scheme unquote does,
as of R6RS. The splicing case is similar. If [xi] looks like ,@form
it is simply reduced to form. So ,@,@form becomes ,@form.
Thus if we expand the inner backquote of this, and leave the outer one
alone, then this:
``(,,a ,,@b ,@,@c)
becomes this:
`(append (list ,a) (list ,@b) ,@c)
Thus if C is bound to a list of forms, they all get spliced into this
append. And so in the next evaluation, each of these forms (except the
last one) must produce lists which are spliced together. Thus the ,@
distributes into the ,@c.
| |
| Pascal Costanza 2008-02-15, 7:18 pm |
| andreuri2000@yahoo.com wrote:
> Let me point out that the simplicity of defmacro is
> sometimes exaggerated when compared to define-syntax.
> For example, the following let-macro
>
> (define-syntax let
> (syntax-rules ()
> ((_ ((x v) ...) b1 b2 ...)
> ((lambda (x ...) b1 b2 ...) v ...))))
>
> is /not/ the same as the superficially simple
>
> (define-macro (let bindings . body)
> `((lambda ,(map car bindings) ,@body) ,@(map cdr bindings)))
>
> but rather expresses the following:
>
> (define-macro (let . rest)
> (unless
> (and (list? rest)
> (>= (length rest) 2))
> (syntax-error 'let
> "Does not match (((x v) ...) b1 b2 ...)" rest))
> (unless
> (and (list? (car rest))
> (andmap (lambda (binding)
> (= (length binding) 2))
> (car rest)))
> (syntax-error 'let
> "Does not match ((x v) ...)" (car rest)))
> `((lambda ,(map car (car rest)) ,@(cdr rest))
> ,@(map cdr (car rest))))
Too complicated.
(defmacro let ((&rest bindings) &body body)
(loop for (var val . rest) in bindings
collect var into vars
collect val into vals
do (assert (null rest) ()
"Too many values for ~S." var)
finally (return `((lambda ,vars ,@body) ,@vals))))
LOOP is a pretty versatile tool for macro programming.
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Alan Crowe 2008-02-15, 7:18 pm |
| andreuri2000@yahoo.com writes:
> and a little quiz:
>
> (let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x)) => ??
>
I thought I nearly knew this,
`(,(a b c) ,@(a b c) ,a ,b ,c and err, not sure...
but CMUCL is giving me either
`(,(A B C) ,@(A B C) ,A ,@B) with 19a
or
`(,(A B C) (,@(A B C)) (,A ,@B)) with 19c
while
SBCL says `(,(A B C) ,@(A B C) ,A ,B ,C ,@A ,@B ,@C)
which is convincing
and CLISP says
`(,(A B C) ,@(A B C) (SYSTEM::UNQUOTE A B C) ,@'(SYSTEM::UNQUOTE A B C))
so I'm wondering if there is a bug in CMUCL.
Alan Crowe
Edinburgh
Scotland
| |
| Jeff Barnett 2008-02-15, 7:18 pm |
| Pascal Costanza wrote:
> andreuri2000@yahoo.com wrote:
>
> Too complicated.
>
> (defmacro let ((&rest bindings) &body body)
> (loop for (var val . rest) in bindings
> collect var into vars
> collect val into vals
> do (assert (null rest) ()
> "Too many values for ~S." var)
> finally (return `((lambda ,vars ,@body) ,@vals))))
>
> LOOP is a pretty versatile tool for macro programming.
>
> Pascal
>
I'm Lisp curious, if there are any declarations (or strings) in the body
portion of this macro, will the behavior of the let and the lambda be
guaranteed identical?
-- Jeff Barnett
| |
| Kaz Kylheku 2008-02-15, 7:18 pm |
| On Feb 15, 1:09 pm, Alan Crowe <a...@cawtech.freeserve.co.uk> wrote:
> andreuri2...@yahoo.com writes:
>
>
> and CLISP says
>
> `(,(A B C) ,@(A B C) (SYSTEM::UNQUOTE A B C) ,@'(SYSTEM::UNQUOTE A B C))
Wow, how old is your CLISP? That looks suspiciously like the old
backquote implementation that was replaced some five years ago.
Now I'm a bit out of date myself here with 2.38, but I get:
[1]> (let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x))
(CONS (A B C) (APPEND (A B C) (LIST* A B C (APPEND A B C))))
It's not as pretty an answer as SBCL's, but right.
| |
| andreuri2000@yahoo.com 2008-02-15, 10:13 pm |
| On Feb 15, 4:12 pm, Pascal Costanza <p...@p-cos.net> wrote:
> andreuri2...@yahoo.com wrote:
>
[color=darkred]
> (defmacro let ((&rest bindings) &body body)
> (loop for (var val . rest) in bindings
> collect var into vars
> collect val into vals
> do (assert (null rest) ()
> "Too many values for ~S." var)
> finally (return `((lambda ,vars ,@body) ,@vals))))
This does not appear equivalent. What happened to the elegant
failure if a binding is not a list, or has length less than 2?
Also, what happened to the test that the body not be empty?
Andre
| |
| Kent M Pitman 2008-02-15, 10:13 pm |
| Kaz Kylheku <kkylheku@gmail.com> writes:
> On Feb 15, 1:09 pm, Alan Crowe <a...@cawtech.freeserve.co.uk> wrote:
>
> Wow, how old is your CLISP? That looks suspiciously like the old
> backquote implementation that was replaced some five years ago.
>
> Now I'm a bit out of date myself here with 2.38, but I get:
>
> [1]> (let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x))
> (CONS (A B C) (APPEND (A B C) (LIST* A B C (APPEND A B C))))
>
> It's not as pretty an answer as SBCL's, but right.
Just eyeballing it, I expected:
(let ((a '(a1 a2 a3))
(b '(b1 b2 b3))
(c '(c1 c2 c3)))
(flet ((a (&rest foo) `(a-fun ,foo))
(b (&rest foo) `(b-fun ,foo))
(c (&rest foo) `(c-fun ,foo)))
(macrolet ((show-it ()
(let ((x '(a b c))) ``(,,x ,@,x ,,@x ,@,@x))))
(show-it))))
`(,(a b c) ,@(a b c) ,a ,b ,c ,@a ,@b ,@c)
to return the same as:
(let ((a '(a1 a2 a3))
(b '(b1 b2 b3))
(c '(c1 c2 c3)))
(flet ((a (&rest foo) `(a-fun ,foo))
(b (&rest foo) `(b-fun ,foo))
(c (&rest foo) `(c-fun ,foo)))
(macrolet ((show-it ()
'(append (list (a b c))
(a b c)
(list a b c)
a b c)))
(show-it))))
.... and it seems to do that.
No, I don't recommend using these idioms all the time. But that's not
how they come up. They happen gradually.
Complicated syntax isn't there because you're always supposed to use it.
It's there so that if you drift into it gradually you won't suddenly fall
off a cliff and have to suddenly be forced to use a different syntax.
You can make very long sentences in English (and other natural
languages), too, even though style guides will tell you only to use a
subset of what you theoretically can do.
| |
| Alan Crowe 2008-02-16, 4:29 am |
| Kaz Kylheku <kkylheku@gmail.com> writes:
> On Feb 15, 1:09 pm, Alan Crowe <a...@cawtech.freeserve.co.uk> wrote:
>
> Wow, how old is your CLISP? That looks suspiciously like the old
> backquote implementation that was replaced some five years ago.
>
[1]> (lisp-implementation-version)
"2000-03-06 (March 2000)"
Yes, I've fallen behind a little. I think I mentioned
recently that I've not been well.
Alan Crowe
Edinburgh
Scotland
| |
|
|
| Pascal Costanza 2008-02-16, 8:12 am |
| andreuri2000@yahoo.com wrote:
> On Feb 15, 4:12 pm, Pascal Costanza <p...@p-cos.net> wrote:
>
>
> This does not appear equivalent.
Sure. This is Common Lisp, not Scheme. Things are expected to behave a
bit different over here. (That's one thing Schemers seem to find hard to
get: When Common Lispers say that defmacro is good, they mean defmacro,
not define-macro. There are some strong differences between the Common
Lisp version and the Scheme versions.)
> What happened to the elegant
> failure if a binding is not a list, or has length less than 2?
> Also, what happened to the test that the body not be empty?
- If a binding is not a list: That's covered by the destructuring
expression in the LOOP form.
(let (a b c) a)
*** - CAR: A is not a list
Actually, that's not what a Common Lisper would expect. A Common Lisper
would expect that the variables are accepted anyway and implicitly bound
to nil. This is easy to achieve. Define the following function:
(defun prepare-binding (binding)
(if (consp binding) binding (list binding)))
And loop over (mapcar #'prepare-binding bindings) instead of just bindings.
- If a binding has length less than 2: A Common Lisper would expect an
implicit binding to nil, and this is what happens here.
(let ((a)) a)
NIL
If you insist on being more strict, you can handle that case in
prepare-binding.
- If the body is empty: Empty lambda bodies implicitly return nil.
That's what Common Lispers would expect.
>((lambda ()))
NIL
Again, if you insist on being more strict, just put an (assert body) in
the beginning of the macro definition. You'll get a nice error message then.
The implicit rules are there to make programming with such constructs
more convenient (and convenience is what new language constructs are all
about). Syntax-rules is nice for simple macros, but as soon as you want
to add more bells and whistles, it becomes harder to do so. (How do you
localize the different cases how to treat the different variations of
what a binding could look like?)
Scheme has a tendency to disapprove of bells and whistles, but Common
Lisp encourages them. Whether that's a good idea or not is a subjective
assessment.
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| andreuri2000@yahoo.com 2008-02-16, 7:23 pm |
| On Feb 16, 7:02 am, Pascal Costanza <p...@p-cos.net> wrote:
>
> That's one thing Schemers seem to find hard to
> get: When Common Lispers say that defmacro is good, they mean defmacro,
> not define-macro.
Okay, but comparing the above syntax-rules to your defmacro,
I personally find the syntax-rules more concise and legible
(effectively only two lines of BNF-like notation). YMMV.
>
> - If a binding is not a list: That's covered by the destructuring
> expression in the LOOP form.
>
> (let (a b c) a)
>
> *** - CAR: A is not a list
That's not quite what was meant by elegant failure, though...
> Actually, that's not what a Common Lisper would expect. A Common Lisper
> would expect that the variables are accepted anyway and implicitly bound
> to nil.
>
> - If a binding has length less than 2: A Common Lisper would expect an
> implicit binding to nil,
Good grief.
Here is an idiomatic way of expressing that, trivially, in Scheme
(R6RS).
(define-syntax let
(lambda (form)
(syntax-case form ()
((_ (b ...) e ...)
(with-syntax
((((x v) ...)
(map (lambda (b)
(syntax-case b ()
((x v) (identifier? x) #'(x v))
((x) (identifier? x) #'(x '()))
(x (identifier? x) #'(x '()))))
#'(b ...))))
#'((lambda (x ...) e ...) v ...))))))
I don't think this is so much about fundamental properties of
macro systems as it is about programming style.
Pattern matching is just as easy to use in Lisp defmacros
as it is in Scheme, yet it has never caught on.
Why Lispers prefer not to use it, and many Schemers do,
is probably more of a cultural question than any
fundamental property of defmacro.
One could just as well ask why Schemers themselves like to use
patterns for macros yet prefer not to use pattern matching for
function definitions as in ML.
Andre
| |
| Pascal Costanza 2008-02-16, 7:23 pm |
| andreuri2000@yahoo.com wrote:
> On Feb 16, 7:02 am, Pascal Costanza <p...@p-cos.net> wrote:
>
> Okay, but comparing the above syntax-rules to your defmacro,
> I personally find the syntax-rules more concise and legible
> (effectively only two lines of BNF-like notation). YMMV.
Sure, for small and simple macros, syntax-rules is better. I'm not
convinced about more complex macros. (Schemers seem to have a tendency
towards a stronger functional programming style, so that may not be as
important then.)
>
> That's not quite what was meant by elegant failure, though...
What is inelegant about that?
>
> Good grief.
>
> Here is an idiomatic way of expressing that, trivially, in Scheme
> (R6RS).
>
> (define-syntax let
> (lambda (form)
> (syntax-case form ()
> ((_ (b ...) e ...)
> (with-syntax
> ((((x v) ...)
> (map (lambda (b)
> (syntax-case b ()
> ((x v) (identifier? x) #'(x v))
> ((x) (identifier? x) #'(x '()))
> (x (identifier? x) #'(x '()))))
> #'(b ...))))
> #'((lambda (x ...) e ...) v ...))))))
See? Now the Scheme macro becomes more complicated as well...
> I don't think this is so much about fundamental properties of
> macro systems as it is about programming style.
Agreed.
> Pattern matching is just as easy to use in Lisp defmacros
> as it is in Scheme, yet it has never caught on.
> Why Lispers prefer not to use it, and many Schemers do,
> is probably more of a cultural question than any
> fundamental property of defmacro.
Right.
> One could just as well ask why Schemers themselves like to use
> patterns for macros yet prefer not to use pattern matching for
> function definitions as in ML.
As I said elsewhere in this thread (I think), syntax-rules is nice for
simple cases, which is exactly what you need for example in papers or
other situations where you want to illustrate something in neat and
brief ways. I guess, syntax-rules is probably also somewhat nicer to
read and understand by non-Lispers/Schemers than define-macro/defmacro,
which helps for exposition as well.
In practical settings (and when not being fixated on a functional
programming style), you need a 'real' macro system (whichever concrete
macro system that may be). The reason is that it's more important that
the macros are convenient to use, rather than that it's convenient to
write those macros.
Pascal
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| John Thingstad 2008-02-16, 7:23 pm |
| På Sat, 16 Feb 2008 19:35:46 +0100, skrev <andreuri2000@yahoo.com>:
> Good grief.
>
> Here is an idiomatic way of expressing that, trivially, in Scheme
> (R6RS).
>
> (define-syntax let
> (lambda (form)
> (syntax-case form ()
> ((_ (b ...) e ...)
> (with-syntax
> ((((x v) ...)
> (map (lambda (b)
> (syntax-case b ()
> ((x v) (identifier? x) #'(x v))
> ((x) (identifier? x) #'(x '()))
> (x (identifier? x) #'(x '()))))
> #'(b ...))))
> #'((lambda (x ...) e ...) v ...))))))
>
We must have different ideas of trivial..
> One could just as well ask why Schemers themselves like to use
> patterns for macros yet prefer not to use pattern matching for
> function definitions as in ML.
I wonder about that one myself.
--------------
John Thingstad
| |
| andreuri2000@yahoo.com 2008-02-16, 7:23 pm |
| On Feb 16, 2:07 pm, Pascal Costanza <p...@p-cos.net> wrote:
> andreuri2...@yahoo.com wrote:
>
>
> What is inelegant about that?
The message does not even indicate that the error was a syntax error.
> As I said elsewhere in this thread (I think), syntax-rules is nice for
> simple cases, which is exactly what you need for example in papers or
> other situations where you want to illustrate something in neat and
> brief ways. I guess, syntax-rules is probably also somewhat nicer to
> read and understand by non-Lispers/Schemers than define-macro/defmacro,
> which helps for exposition as well.
>
> In practical settings (and when not being fixated on a functional
> programming style), you need a 'real' macro system (whichever concrete
> macro system that may be). The reason is that it's more important that
> the macros are convenient to use, rather than that it's convenient to
> write those macros.
As has been said elsewhere, Scheme has, for years, had 'real' macro
systems that are not syntax-rules, making macros convenient to write
and
use. The Scheme example I wrote of the generalized binding type LET
was
certainly not written in syntax-rules. Comparing defmacro to syntax-
rules
is simply being out of date. Scheme macros are considered by Schemers
to
be more robust than Lisp defmacros, given the referential
transparency
guarantees of Scheme macros, although I know Lispers tend not to care
about this issue.
Andre
| |
| andreuri2000@yahoo.com 2008-02-16, 7:23 pm |
| On Feb 16, 2:09 pm, "John Thingstad" <jpth...@online.no> wrote:
> P=E5 Sat, 16 Feb 2008 19:35:46 +0100, skrev <andreuri2...@yahoo.com>:
>
>
>
> We must have different ideas of trivial..
I can assure you that a Scheme programmer
familiar with syntax-case would find this utterly trivial.
I am not sure how this macro might be expressed idiomatically
in Lisp, but probably a Lisper would find the result more
transparent than the above, while I would probably find it much
less trivial. Again, that is cultural, and it would be silly
to argue that Scheme macros are, therefore, inferior.
>
> I wonder about that one myself.
Perhaps because Schemers tend to come from more academic backgrounds
where using a BNF-type notation for syntax comes naturally.
On the other hand, there is no built-in emphasis on algebraic data
types as in ML.
Andre
| |
| Jeff Barnett 2008-02-16, 7:23 pm |
| Pascal Costanza wrote:
> Jeff Barnett wrote:
>
> Yes, including special declarations.
>
>
> Pascal
>
The reason I asked was historical. When the common lisp standard was
being debated, there was a difference. Declarations within a let could
effect the calculation of binding presets; on the other hand,
declarations in a lambda could not effect the evaluation of its
arguments. Way back when, I argued that this was a semantic glitch. I'm
glad to hear the problem was finally resolved.
-- Jeff Barnett
| |
| Kent M Pitman 2008-02-16, 7:23 pm |
| [ comp.lang.lisp only; http://www.nhplace.com/kent/PFAQ/cross-posting.html ]
Jeff Barnett <jbbrus@ca.rr.com> writes:
> Pascal Costanza wrote:
> The reason I asked was historical. When the common lisp standard was
> being debated, there was a difference. Declarations within a let could
> effect the calculation of binding presets; on the other hand,
> declarations in a lambda could not effect the evaluation of its
> arguments. Way back when, I argued that this was a semantic
> glitch. I'm glad to hear the problem was finally resolved.
Hmm. My recollection is that Jeff is right. I'll have to look into it
more. I think the difference is related to the scope of the specials
onto the init values. In some cases, I think the special can go into
the init value. But maybe I'm misremembering.
I tried this in LWW 4.4.5 and LWW 5.0.1 and I was surprised by the answer.
(let ((a 'outer))
(declare (special a))
(let ((a 'inner))
;; this a is lexical
(let ((b a))
(declare (special a))
(list b (symbol-value 'a)))))
=> (INNER OUTER)
I would have expected the answer to be (OUTER OUTER), but maybe I'm wrong.
I don't have time investigate this right now, so I figured I'd put it up and
maybe someone could do the research while I was off doing other things. :)
But in any case, my point is that I think there are places where
(let (...) (declare ...) ...)
is not the same as
((lambda (...) (declare ...)) ...)
becuase the declare is not spanning the declarations in the lambda case,
and might need to be.
Disclaimer: No one should take any of my "I think"'s in this as some
sort of claim of truth. They're just my recollection and impression
of the moment. I reserve the right to change my mind.
| |
| John Thingstad 2008-02-16, 7:23 pm |
| På Sat, 16 Feb 2008 21:32:26 +0100, skrev <andreuri2000@yahoo.com>:
> is simply being out of date. Scheme macros are considered by Schemers
> to be more robust than Lisp defmacros, given the referential
> transparency guarantees of Scheme macros, although I know Lispers tend
> not to care
> about this issue.
>
We care. We just take care of it through protocol of writing.
gensym is a bit lightweight. With the help of a couple of macroes named
'with-unique-names' and 'rebinding' it is fairly easy to write
referentially transparent code. Rebinding for rebinding input variables so
they are evaluated before use. with-unique-values for introducing new
variables.
(defmacro lister (p q)
(with-unique-names (x y)
`(let ((,x (x-function))
(,y (y-function)))
(list ,p ,q ,x ,y))))
the form (lister i j) macroexpands to
(LET* ((#:X-88 (X-FUNCTION))
(#:Y-89 (Y-FUNCTION)))
(LIST i j #:X-88 #:Y-89))
(defmacro lister (x y)
(rebinding (x y)
'(list ,x ,y)))
the form (lister i j) macroexpands to
(LET* ((#:X-77 I)
(#:Y-78 J))
(LIST #:X-77 #:Y-78))
This seems to cover most cases.
--------------
John Thingstad
| |
| Pascal Costanza 2008-02-16, 10:22 pm |
| John Thingstad wrote:
> På Sat, 16 Feb 2008 21:32:26 +0100, skrev <andreuri2000@yahoo.com>:
>
>
> We care. We just take care of it through protocol of writing.
> gensym is a bit lightweight. With the help of a couple of macroes named
> 'with-unique-names' and 'rebinding' it is fairly easy to write
> referentially transparent code. Rebinding for rebinding input variables
> so they are evaluated before use. with-unique-values for introducing new
> variables.
[...]
> This seems to cover most cases.
No, that's not enough. What you list here solves the problems of
unintended variable capture, and order and number of evaluations.
Referential transparency is something else, though.
Consider:
(let ((x 42))
(macrolet ((foo () 'x))
(let ((x 4711))
(foo))))
Now assume you're not allowed to change the names of the x variable
bindings. In a defmacro-style macro system, there is no way that you can
ensure that the code to which foo expands refers to the outer x by just
changing the macro definition. Whatever you may try, the outer x will be
shadowed by the inner x in the inner expansion of foo.
Hygienic macro systems solve this issue. The following evaluates to 42:
(let ((x 42))
(let-syntax ((foo (syntax-rules () ((foo) x))))
(let ((x 4711))
(foo))))
The problem can be relatively easily circumvented in Common Lisp by
using the package system and choosing names more carefully. After all,
the following actually does what you expect:
(let ((x 42))
(macrolet ((foo () 'x))
(let ((y 4711)) ; note: name changed
(foo))))
If Common Lisp had a module system instead of a package system, and if
Common Lisp were a Lisp-1 instead of a Lisp-2, referential transparency
would be a much more pressing issue. [1]
Nevertheless, hygienic macro systems are strictly more powerful in that
regard than defmacro-style macro systems.
Pascal
[1] That's the main reason why I personally prefer packages over modules
and Lisp-2 over Lisp-1, because I get much less nameclashes with that in
general (not only in macros).
--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
| |
| Juho Snellman 2008-02-16, 10:22 pm |
| Kent M Pitman <pitman@nhplace.com> writes:
> I tried this in LWW 4.4.5 and LWW 5.0.1 and I was surprised by the answer.
>
> (let ((a 'outer))
> (declare (special a))
> (let ((a 'inner))
> ;; this a is lexical
> (let ((b a))
> (declare (special a))
> (list b (symbol-value 'a)))))
> => (INNER OUTER)
>
> I would have expected the answer to be (OUTER OUTER), but maybe I'm wrong.
> I don't have time investigate this right now, so I figured I'd put it up and
> maybe someone could do the research while I was off doing other things. :)
Lispworks is correct. The inner SPECIAL A declaration is a free
declaration, and per 3.3.4:
The scope of free declarations specifically does not include
initialization forms for bindings established by the form containing
the declarations.
--
Juho Snellman
| |
| Maciej Katafiasz 2008-02-16, 10:22 pm |
| Den Sat, 16 Feb 2008 19:43:35 -0500 skrev Kent M Pitman:
> I tried this in LWW 4.4.5 and LWW 5.0.1 and I was surprised by the
> answer.
>
> (let ((a 'outer))
> (declare (special a))
> (let ((a 'inner))
> ;; this a is lexical
> (let ((b a))
> (declare (special a))
> (list b (symbol-value 'a)))))
> => (INNER OUTER)
>
> I would have expected the answer to be (OUTER OUTER), but maybe I'm
> wrong. I don't have time investigate this right now, so I figured I'd
> put it up and maybe someone could do the research while I was off doing
> other things. :)
No actual research, but this is consistent with my expectations. I see
declarations as happening conceptually at the same time as bindings, in
which case b can't see the value of special a, as it doesn't exist at the
time b is bound. Harder to decide would be what to do with a LET*,
though, there I fail to have any consistent expectations and would have
to consult the spec :)
Cheers,
Maciej
| |
| Rob Warnock 2008-02-16, 10:22 pm |
| Pascal Costanza <pc@p-cos.net> wrote:
+---------------
| (let ((x 42))
| (macrolet ((foo () 'x))
| (let ((x 4711))
| (foo))))
|
| Now assume you're not allowed to change the names of the x variable
| bindings. In a defmacro-style macro system, there is no way that you can
| ensure that the code to which foo expands refers to the outer x by just
| changing the macro definition. Whatever you may try, the outer x will be
| shadowed by the inner x in the inner expansion of foo.
+---------------
Not to disagree with your point about macro scope & shadowing,
but this paticular example is not at all compelling, since you
could easily have gotten the desired result with no macros at all!
> (let ((x 42))
(flet ((foo () x))
(let ((x 4711))
(foo))))
42
> (let ((x 42))
(flet ((foo () x))
(let ((x 4711))
(flet ((bar () x))
(let ((x 937))
(list (foo) (bar) x))))))
(42 4711 937)
>
Look, Ma, no macros!
Sometimes I think that with all the bickering about macro style
we sometimes forget that Scheme & CL both share the amazing power
of lexical closures.
+---------------
| Hygienic macro systems solve this issue.
+---------------
But so do closures... and with less "new syntax".
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
| |
| Kent M Pitman 2008-02-17, 5:03 am |
| Maciej Katafiasz <mathrick@gmail.com> writes:
> Den Sat, 16 Feb 2008 19:43:35 -0500 skrev Kent M Pitman:
>
>
> No actual research, but this is consistent with my expectations.
I think it was not the rule for some time, perhaps in CLTL times.
Looks like we did fix it. Must have left a bad taste in my mouth.
The prevailing issue was Issue DECLARATION-SCOPE:NO-HOISTING.
http://www.lispworks.com/documentat...es/iss092_w.htm
> I see declarations as happening conceptually at the same time as
> bindings,
Well, that's my point. Since there is no binding, it seems to me that
we language designers should have defaulted to applying to the whole
LET (since there is no binding to stop it) rather than the case of
interest is.
> in which case b can't see the value of special a, as it doesn't
> exist at the time b is bound.
Point of view, I suppose. I think of the value of special A as
"always existing" since it is always accessible via symbol-value, a
clue that all specials with the same name share the same value cell.
The variables never come into existence, only the ability to reference
the | | |