[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Chicken-hackers] CR: (#439) quasiquote changes

From: Peter Bex
Subject: [Chicken-hackers] CR: (#439) quasiquote changes
Date: Tue, 7 Dec 2010 12:46:29 +0100
User-agent: Mutt/


This mail is an announcement that ticket #439 is a change request
because it may break existing code.

I'd also like some feedback on what the change (if any) should look
like.  The issue is that quasiquote and unquote discarded extra
arguments to the procedure and are not strictly R5RS compliant.
See the ticket for more info.

I also tried to make the behaviour of quotation/unquotation nesting
more consistent with my patch.

Basically, according to R5RS, nested calls to quasiquote within
a quasiquote should increase the nesting level by 1, and unquotation
(unquote or unquote-splicing) decrease the nesting level by 1.
Only when the nesting level is zero, the argument to unquote or
unquote-splicing is evaluated.

R5RS explicitly mentions that nested quasiquote, unquote or
unquote-splicing calls in positions other than the head of a proper
list which contains two elements is undefined and can lead to
strange behaviour:

There are a few options on how we could improve our quasiquote

Option 1:
As (quasiquote a b c) throws an error, so could
(quasiquote (quasiquote a b c)) or any even deeper-nested quasiquotes,
as well as unquotes.  Having multiple arguments to quasiquote is
not allowed on level 0, so why should it be allowed on higher levels?
This is the pedantically correct option and might ease the debugging
of code that contains quasiquote calls because this code would be
unportable.  This could break existing code.

This is probably the cleanest option from a language purity standpoint.
It would also make the code even a bit simpler than my patch does, I
think.  We could put a call to ##sys#check-syntax after it's determined
that the car of a pair contains one of the three words quasiquote,
unquote or unquote-splicing.  Then we have an if which checks the
nesting level and either calls WALK recursively or does the unquoting.

The disadvantage of this option compared to option 2 is that it
will be a little less practical when you want to use the literal
symbols quasiquote, unquote or unquote-splicing in deeper-nested
With option 2, (quasiquote (x y (quasiquote a b c)))
will "just work".  If this option would be implemented, this
would need to be written as
(quasiquote (x y (unquote-splicing (quote (quasiquote a b c)))))
or as
(quasiquote (x y (unquote (quote quasiquote)) a b c))
otoh, when written as `(x y ,'quasiquote a b c) that's not too

However, something like (quasiquote (x quasiquote y z)) would
also (perhaps unexpectedly) give an error as that's identical to
(quasiquote (x . (quasiquote y z)))
This difference cannot be detected because quasiquote, unlike normal
Scheme code, allows improper lists to occur anywhere.  In normal Scheme
(x . (y z)) is identical to (x y z) and the y is never evaluated
as a macro.

Option 2 (this is what my current patch does):
Only throw errors in case of invalid list arguments at level 0
and increase the level with 1 whenever quasiquote is found at the
car of a pair.  It will allow you to write quasiquotes and unquotes
at nesting levels above 1 without any strange trickery, and will
throw a nice error message when you try to use them at level 0 with
wrong arity.

The example above, (quasiquote (x quasiquote y z)), will just work
and not complain.  However,  (quasiquote (x quasiquote y z (unquote a)))
will not unquote the a, because this is identical to
(quasiquote (x . (quasiquote y z (unquote a)))), so (unquote a) occurs
at nesting level 1 and simply decreases the level to 0 instead of
evaluating the "a".  That's the price we'd pay for having a loose and
non-strictly pedantic system.

Option 3:
Only fix R5RS behaviour and throw an error when an invalid number
of arguments are found at level 0.  This will undo the part of my
patch that tries to make the behaviour more consistent.  I am unsure
right now whether this is possible at all or if the inconsistencies
are what actually cause the r5rs breakage.

The main difference is that the code in quasiquote stays the same; it
will distinguish between quasiquote at the car of a pair which has a
pair as cdr and quasiquote at the car of a pair which has a non-pair
as cdr.  Example: (quasiquote (quasiquote . #((unquote a))))
will result in trying to unquote a, whereas in options 1 and 2 it
will stay quoted because the second quasiquote increases the nesting
level.  I can't think of any other cases than vectors where this
differs though (unless extra read syntax is added later).

Option 4:
Only add an error message when wrong number of arguments are found
at level 0.  This would keep the R5RS-incompatibilities.

Option 5:
Do not change anything.  It is not a bug, r5rs does not specify
what happens so silently dropping extra arguments to unquote is

Option 6:
Do not change the behaviour of unquote with multiple args, but do
fix the behaviour of nested quasiquotes to pass only the new
r5rs-compliance tests.  This is option 3, but will leave the
behaviour that (quasiquote (quasiquote a b c)) => (quasiquote a)

In all of the above options, the fix for the "f" bug in
syntax-tests will be applied of course.

"The process of preparing programs for a digital computer
 is especially attractive, not only because it can be economically
 and scientifically rewarding, but also because it can be an aesthetic
 experience much like composing poetry or music."
                                                        -- Donald Knuth

reply via email to

[Prev in Thread] Current Thread [Next in Thread]