[plt-scheme] On hygiene and trust

From: Eli Barzilay (eli at barzilay.org)
Date: Thu Jul 9 15:28:43 EDT 2009

On Jul  9, Robby Findler wrote:
> >>> And when I go in the other direction, I have the symbol `foo' and I
> >>> wish to create some code that uses it, I have to transform it into a
> >>> syntax object with the appropriate scope.
> >>
> >> (Same as above -- you need to add the extra information.)
> >
> > But why?  Surely there is a reasonable default.
> >
> I think that there isn't any reasonable default. If you are turning
> a raw sexpression (ie, a bit of program text without any lexical
> information) into a syntax object, you need to say what scope it
> comes from explicitly.

I think that another way to put this is that the lack of a reasonable
default is exactly why `defmacro' is inherently broken and why
`syntax-rules' is inherently weak.

Joe replies:
> How about an unreasonable default, then?  Always use the scope of
> the macro and never use the scope of the call-site unless explicitly
> overridden.  Or if that's too cumbersome, then the other way.

And these two -- IIUC -- are exactly the two extremes, which means
that you still need some way to specify things between them.


On Jul  9, Joe Marshall wrote:
> On Thu, Jul 9, 2009 at 10:47 AM, Eli Barzilay<eli at barzilay.org> wrote:
> >> I can't `mapcar' over a syntax object.
> >
> > Sure you can, use `syntax->list' with `map'.
> 
> That's not exactly the same.  Sure, I can call syntax->list and then
> call list->syntax afterwards (I assume), but I can't just call
> mapcar.

Sorry, I just don't see how that's different than using `map' over a
string, for example...  Maybe it's the fact that you choose to accept
the extra complexity with strings because you see a point in having it
be a different type, but for some reason you refuse to divorce syntax
from lists in the same way?


> > This is the same thing that drives me crazy over and
> > over.  There's hardly any magic in `syntax-case' -- and in fact,
> > you could just as well write macros in a `syntax-case'-based
> > systems without ever using it.  For example:
> >  http://blog.plt-scheme.org/2009/05/explicit-renaming-macros-implicitly.html
> 
> This entry makes my point exactly.  syntax-case is just too
> low-level.  Sure, defmacro is even *lower* level, but that makes it
> easier to understand.

??  `syntax-case' is just a tool to do pattern matching on syntax
values, however they may be represented.  It's high level enough to be
convenient (for example, expressing the same kind of macros that you
can with `syntax-rules' is trivial), yet you can still do things like:

  (define (stx-car stx)
    (syntax-case stx
      [(x . y) #'x]))

without knowing about the internal representation.  (BTW, in PLT this
function is defined with

  (define (stx-car p)
    (if (pair? p)
      (car p)
      (car (syntax-e p))))

so the only thing you need to know about to use the representation is
the `syntax-e' function -- the rest is mostly conveniences.)


> Suppose I want a macro that examines one of its arguments to see if it
> is a lambda expression.  The ideal thing, of course, is a function that
> does just that:
> 
> (define-syntax foo
>   (lambda (stx)
>     (if (lambda-expression? (first-argument stx))
>         ....)))

You still get that:

  > (define-syntax (foo stx)
      (if (free-identifier=? #'lambda (stx-car (stx-cdr stx)))
        #'1
        #'2))
  > (foo +)
  2
  > (foo lambda)
  1

except that `syntax-case' is much easier to deal with than
`stx-c[ad]r', and it adds more stuff (like throwing a syntax error) if
the input syntax doesn't have a `cadr':

    (define-syntax (foo stx)
      (syntax-case stx ()
        [(_ id) (if (free-identifier=? #'lambda #'id)
                  #'1
                  #'2)]))

but as long as we're doing a single `if' expression, we can just as
well use the fender-exprs of `syntax-case':

    (define-syntax (foo stx)
      (syntax-case stx ()
        [(_ id) (free-identifier=? #'lambda #'id) #'1]
        [_ #'2]))

but even better, that kind of facility is "built into" the
`syntax-case' tool in the same way as `syntax-rules':

    (define-syntax (foo stx)
      (syntax-case stx (lambda)
        [(_ lambda) #'1]
        [_ #'2]))

And yes, now it's easy to see that my example can be expressed with
`syntax-rules' -- but I still get arbitrary expressions that I can
write as the results.  In other words, `syntax-case' is just a very
convenient tool that did all of the good things that `syntax-rules'
did, without sacrificing the ability to write arbitrary code.


On Jul  9, Joe Marshall wrote:
> On Thu, Jul 9, 2009 at 11:36 AM, David Van Horn<dvanhorn at ccs.neu.edu> wrote:
> >
> > Perhaps it is this?
> >
> >   * Wrapped = Unwrapped (r6rs optional):
> >
> >       A wrapped syntax object is the same as an unwrapped syntax
> >       object, and can be directly manipulated using car, cdr, ...
> >       without syntax-case deconstruction.
> >
> >   http://www.het.brown.edu/people/andre/macros/
> 
> Closer.
> 
> What I really want is a set of primitives, means of combination, and
> means of abstraction that work well with code objects.

Sounds like you just want this

  http://docs.plt-scheme.org/syntax/syntax-helpers.html


> [...]  I also want the ability to inject symbols hygienically
> without having to think about it.  I also want the ability to inject
> symbols *non-hygienically*.

But `datum->syntax' does just that...  You start with "identifiers"
being "a symbols plus some lexical information (whatever form that
might have)" -- and to generate new identifiers, all you need is
`datum->syntax' that accepts the symbol that you want to inject plus
the lexical information that you copy from some other piece of syntax,
which is how you get to control the scope without ever knowing how the
lexical information is actually represented.

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


Posted on the users mailing list.