[racket] tutorial: exploring the boundaries of outer space

From: Eli Barzilay (eli at barzilay.org)
Date: Wed Apr 11 11:48:41 EDT 2012

Just now, Brian Mastenbrook wrote:
> On Apr 11, 2012, at 9:41 AM, Eli Barzilay wrote:
> >> Is there a way to make (outer (outer x)) do the right thing while
> >> still using syntax parameters?
> > 
> > That's an obvious bait, right?
> > [...]
> Maybe I'm still a little groggy this morning, but where's the
> `syntax-parameterize'? That looks like the regular datum->syntax
> insertion based version of `def' to me.

(Yes, I was making what you were going for more explicit...)

> One difference is that syntax parameters are much more like Common
> Lisp specials than they are Racket parameters in that the apparent
> binding of the identifier changes rather than the behavior of what
> the identifier is bound to.

I think that you're making the same mistake of confusing the scope of
macro level bindings with the scope of the syntax they create.  (Same
as cases when you're talking about macros that "close over" stuff...)

> What this means in practice is that I *know* that
> (current-output-port) refers to the current value of a parameter,
> since I'm calling it, and there's no expectation that a called
> procedure return the same value every time. On the other hand, I am
> surprised if a simple variable reference in a macro refers to
> different bindings depending on where the macro is called.

In particular, I don't see such a problem: if your macro changes a
syntax parameter, then it's definitely something that should be
documented in how it works.  There's one *major* problem here: it's
too easy to come up with toy examples that make extreme points seem
valid.  OTOH, I haven't seen practical macros that are similarly
confusing.  To clarify, this is not some hand-wavy argument that "in
practice there are no problems" -- instead, it's a meta point that in
proper macros there are less surprises since the intention is obvious.
For example, we used a `loop' macro with an `abort' syntax parameter
-- and that makes it clear that some macros that expand into it, these
uses should "protect" the user code from seeing the `abort' binding
because these macros want to just use loops without exposing that to
users.  In other macros (and IM--subjective--O that's in most cases)
you want user code to see the `abort' -- for example, in new looping
constructs that extend the original and use `abort' too.

But whatever your subjective view is, the problem is that both of
these are valid cases -- so whatever a specific solution does it's
easy to say "meh, it doesn't do *that* right".

> I think macros which wrap around a body or an expression are much
> more common than higher-order-functions, and syntax parameters are
> intended to be used as a lexical convenience in these macros

They're more than just a convenience -- see the example in the paper
of what happens when you don't use them: either hygienically or not,
you end up threading extra (syntactic) values through macros in a way
that is just as impractical as what you get when you don't have

(BWT, your use of "lexical" here looks again like the above

> I think the fact that Danny found the behavior of his macro to be
> surprising speaks for itself, and I'm wondering if other people feel
> the same way too.

I attribute that to the toy example effect.  I think that he'd find
the fact that

  (define-syntax-rule (outer-x) (outer x))

is broken just as surprising.

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the users mailing list.