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

[Scheme-reports] [wg2] in support of single-arity procedural syntax transformers



Hello list,

I would like to argue in favor of single-arity, single-return syntax
transformers.

That is to say, the following should be a valid, though non-terminating,
program:

   (define-syntax id
     (lambda (x)
       x))

As should this:

   (define-syntax else
     (lambda (x)
       (error "else referenced in invalid context")))

Having moved beyond defmacros, though, it should be clear that `x'
should not be a bare s-expression.  You probably want to attach other
information to the s-expression: renamers, compare procedures, wraps,
marks, or source information.  You need a richer data type than
s-expressions.

Single-arity is the right answer for implementers that want to
experiment with different macro systems, because it presents a uniform,
extensible macro transformer interface -- you can add whatever you want
to the syntax object.

It also happens to be the model promoted by R6RS.  Note that the R6RS
does *not* specify a constructor for syntax objects, except for
datum->syntax, which takes all of its "meta" information from another
syntax object.

To do anything useful with these syntax objects, implementations should
provide syntax-e, as Racket does.  Syntax->datum can be built on top of
syntax-e; and implementations that wish to provide ER macros (for
example) can ensure that syntax objects get `rename' and `compare'
procedures associated with them.  This should allow ER in terms of
single-arity procedural transformers.

                              *  *  *

Diplomacy aside, I do think that it would be an error to settle on ER as
the blessed procedural macro transformer for WG2.  It has the wrong
defaults, promotes cadaddring rather than pattern matching, and seems to
be just as complicated to use as syntax-case when it comes to the cases
of fundamental complexity.  If you want beauty, there is syntax-rules;
but anything that has to deal with breaking hygiene is going to have
some essentially complex pieces.

I also do not share Alex's aversion to identifier-syntax.  I have found
it useful in Guile to deprecate top-level bindings:

    (define (read-hash-procedures-warning)
      (issue-deprecation-warning
       "`read-hash-procedures' is deprecated."
       "Use the fluid `%read-hash-procedures' instead."))

    (define-syntax read-hash-procedures
      (identifier-syntax
        (_
         (begin (read-hash-procedures-warning)
                (fluid-ref %read-hash-procedures)))
        ((set! _ expr)
         (begin (read-hash-procedures-warning)
                (fluid-set! %read-hash-procedures expr)))))

I have also found it useful to allow a macro that you usually want
inlined to be referenced by value.  See define-integrable from:

    http://www.scheme.com/tspl4/syntax.html#./syntax:h4

I also found the `method' example from there to be quite compelling.  I
will copy and paste it here.

    (define-syntax method
      (lambda (x)
        (syntax-case x ()
          [(k (ivar ...) formals b1 b2 ...)
           (with-syntax ([(index ...)
                          (let f ([i 0] [ls #'(ivar ...)])
                            (if (null? ls)
                                '()
                                (cons i (f (+ i 1) (cdr ls)))))]
                         [self (datum->syntax #'k 'self)])
             #'(lambda (self . formals)
                 (let-syntax ([ivar (identifier-syntax
                                      [_ (vector-ref self index)]
                                      [(set! _ e)
                                       (vector-set! self index e)])]
                              ...)
                   b1 b2 ...)))])))

Speaking for myself, I had to think about this example pretty hard when
I first saw it, and then when I realized what it did, I was quite
impressed.  Here are the examples that Dybvig gives:

    (let ([m (method (a) (x)
               (list a x self))])
      (m #(1) 2))
    => (1 2 #(1)) 

    (let ([m (method (a) (x)
               (set! a x)
               (set! x (+ a x))
               (list a x self))])
      (m #(1) 2))
    => (2 4 #(2))

In the end, a language is useful inasmuch as it allows expression.
Frankly I think that we get a lot more purchase (in the mechanical
sense) out of syntax-case than out of ER.

                              *  *  *

To return to the original thesis: I don't think that there is anything
wrong with specifying procedural syntax transformers as being of arity
1.  There is nothing specific to syntax-case there.  You always want
more than bare s-expressions; and indeed implementations will make
extensions there.  For example, Racket allows one to see the character
ranges corresponding to a piece of code, including identifier
references, something that is not possible to do with a weak map.  It
would be an error to effectively prohibit that degree of fine source
information in the blessed WG2 macros.

Regards,

Andy
-- 
http://wingolog.org/

_______________________________________________
Scheme-reports mailing list
Scheme-reports@x
http://lists.scheme-reports.org/cgi-bin/mailman/listinfo/scheme-reports