SYNTAX-ATTACKS! Allophones Petrofsky 01 Dec 2003 08:33 UTC

The following message is a courtesy copy of an article
that has been posted to comp.lang.scheme as well.

This response to a SRFI-46 mailing list message is being posted to
comp.lang.scheme and CC'ed to the list.

Taylor Campbell wrote:

> The directive mechanism is a general method for letting expanders
> arbitrarily extend SYNTAX-RULES without hacking kludge after kludge
> onto it in a haphazard manner.


> let me first state that this is just a preliminary idea: it popped
> into my brain early this afternoon and I mentioned it to #scheme
> (where both of those responses were generated), without any formal
> document describing it, any ideas to turn SRFI 46 into a
> SYNTAX-RIASTRADH specification, the thought of sending it to the
> (to-be-chosen) R6RS authors, or anything like that; it was just an
> idea that popped into my head to generalize all the modifications
> that are being considered to SYNTAX-RULES and all of the possible
> future modifications, without continually adding disgusting kludges
> and tacking on some new variant of SYNTAX-RULES whenever someone
> thinks something up.

You seem to think that the significant part of your proposal is the
switch to using keyword-tagged arguments instead of positional ones.
I don't think that's a very radical idea.  I also think that the
(syntax-rules <ellipsis>? <literals> <rule>*) calling convention is
simple enough that switching to a keyword arg list is not necessary
(it's no more complicated than named LET, and people seem okay with
that).  However, adding an additional optional argument (like
UNHYGIENE), probably would push the complexity to the point where
switching to keywords would make sense.

The much more radical part of your proposal is the support for
inserting identifiers unhygienically.  Are you sure you want to open
that can of worms?  Unhygienic insertion really is a dirty and
dangerous business (I've even heard people say it can give you AIDS,
but they might have been talking about something else).

This SRFI started as a codification of some simple existing practice,
namely (... ...).  Then it switched to a new, relatively simple but
untested feature, Choose-Your-Own-Ellipsis.  Now you are talking about
making a major design change that can require significant
implementation effort (eiod and alexpander, for example, would need to
change their whole identifier models to be able to support unhygienic
identifier creation properly) and for which there is no practical

> All I'm doing is putting the idea up for comment.

I hate to get all Bradd W. Szonye on you, but I have to say that
perhaps a SRFI mailing list is the wrong forum in which to put this
idea up for comment.  Maybe you should withdraw the SRFI, discuss
possible designs on comp.lang.scheme, implement some of them and see
how well they work, and then submit a SRFI.

Okay, so now I will proceed to discuss the issues, here in

As for the name, I was thinking that, in light of unhygienic macros'
tendency to bite one on the ass, maybe a warning exclamation point
should be in the name of any hygiene-breaking macro generator.  Hence,
SYNTAX-ATTACKS! (with the all-caps version officially encouraged), or
syntax-rules-are-made-to-be-broken!, or simply syntax-r0OLZ-D00D!!!.

Doc Shriram said in <> on comp.lang.scheme:

> your UNHYGIENE looks distinctly inferior to the version in
> SYNTAX-CASE.  When you capture IT in IF-IT, how do you tell it what
> the scope of IT is meant to be?  This is one of the problems that
> SYNTAX-CASE addresses with extraordinary elegance, and you don't
> seem to be addressing it at all!

I don't think datum->syntax-object's elegance is extraordinary, and in
fact I think it is a little bit broken with respect to macro-producing
macros, and below I will propose a less-broken, more-elegant version
that could work with syntax-attacks!.  First, however, I will
elaborate on the problem Shriram is talking about.

Consider two macros you might want to write that use your IF-IT:
WHEN-IT and OR2.  (WHEN-IT <test> <expr>+) evaluates <test> and, if
the result is not #f, evaluates each <expr> in sequence with IT bound
to the result.  (OR2 <expr1> <expr2>) is just like standard OR, but
must be given exactly two arguments.

  (define-syntax if-it
    (syntax-attacks! ((unhygiene make-unhygienic))
      ((if-it test then else)
       (let (((make-unhygienic it) test))
         (if (make-unhygienic it) then else)))))

  (define-syntax when-it
    (syntax-rules ()
      ((when-it test expr ...)
       (if-it test (begin expr ...) (if #f #f)))))

  (define-syntax or2
    (syntax-rules ()
      ((or2 expr1 expr2)
       (if-it expr1 it expr2))))

When-it works fine, but when or2 calls if-it, if-it inserts a binding
for IT that *won't* capture the renamed IT inserted by or2, and *will*
capture any uses of IT in expr2.  We can fix the first problem like

  (define-syntax or2
    (syntax-attacks! ((unhygiene ick!))
      ((or2 expr1 expr2)
       (if-it expr1 (ick! it) expr2))))

but this still leaves the problem of (let ((it 1)) (or2 #f it))
evaluating to #f (the value to which if-it binds IT), when it should
evaluate to 1.

To fix this in the manner that the syntax-case system does, you would
make the unhygiene operator (our equivalent of datum->syntax-object)
take two arguments (the first one being an identifier whose coloring
should be copied to the second), and have macro invocation pass to the
macro the identifier that was bound to the macro.  Then the macros
could be written like so:

  (define-syntax if-it
    (syntax-attacks! ((unhygiene ick!))
      ((_ test then else)
       (let (((ick! _ it) test))
         (if (ick! _ it) then else)))))

  (define-syntax when-it
    (syntax-attacks! ((unhygiene ick!))
      ((_ test expr ...)
       ((ick! _ if-it) test (begin expr ...) (if #f #f)))))

  (define-syntax or2
    (syntax-rules ()
      ((or2 expr1 expr2)
       (if-it expr1 it expr2))))

In practice (assuming we do not allow identifier concatenation), I
think the first argument to the unhygiene operator will always be the
identifier that was used to invoke the macro.  (That statement might
be way off: someone with more syntax-case experience please correct me
if so, and provide motivation for other first arguments to
datum->syntax-object.)  Therefore, we can make syntax-attacks!
automatically do this.  Then you do away with the first argument to
the unhygiene operator, and also do away with the need to actually
bind the first element of the pattern to anything.  That reduces our
macros to this:

  (define-syntax if-it
    (syntax-attacks! ((unhygiene ick!))
      ((if-it test then else)
       (let (((ick! it) test))
         (if (ick! it) then else)))))

  (define-syntax when-it
    (syntax-attacks! ((unhygiene ick!))
      ((when-it test expr ...)
       ((ick! if-it) test (begin expr ...) (if #f #f)))))

  (define-syntax or2
    (syntax-rules ()
      ((or2 expr1 expr2)
       (if-it expr1 it expr2))))

Now, let me get back to my problem with datum->syntax-object.  It
demands that the second argument be a datum stripped of all marks, but
I think the right thing to do sometimes is to add marks to an
identifier that might already have some.  To enable this,
datum->syntax-object should allow you to pass it three arguments --
two identifiers and a syntax object -- where the second identifier
would always be the same identifier as the first identifier, but
possibly with some extra marks, and these extra marks would be added
to any identifiers in the syntax object.  In the syntax-attacks!
system we could have both of the identifier arguments generated
automatically: they would be the identifier that was bound to the
macro and the identifier that was used in the macro call.

How did I reach this conclusion?  I've run out of time to say.  I'm
afraid I've been "between unemployments" for a whole year now, and
it's really cut back on the time I devote to macrological minutiae.
Fortunately, my long nightmare of personal prosperity will probably
end in January and then I'll be able to dive deeply into this issue
until my sanity boils over.