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

Re: suggestion: a shorter convenience form

This page is part of the web mail archives of SRFI 93 from before July 7th, 2015. The new archives for SRFI 93 contain all messages, not just those from before July 7th, 2015.



Per Bothner skrev:

This has one extra longish keyword, an extra parameter name
mentioned twice, and an extra level of nesting/indentation.

I need that parameter (at least once) for error reporting.

and at
the same time still makes it possible to refer to the original
input syntax-object of the transformer.

This seems to work:

(define-syntax-case name literals
  (form expression))

For example:

(define-syntax-case foo ()
  (form #`(quote form)))

Of course this isn't any better than your "common extension",
but it's not noticeably worse.  And it does allow convenient
pattern-matching, about as convenient and compact as Common Lisp's
defmacro, but allowing multiple "cases".

Consider Kent's implementation of cond in the reference
implementation using the common extension:

  (define-syntax (cond x)
    (syntax-case x ()
      [(_ c1 c2 ...)
       (let f ([c1 #'c1] [c2* #'(c2 ...)])
         (syntax-case c2* ()
           [()
            (syntax-case c1 (else =>)
              [(else e1 e2 ...) #'(begin e1 e2 ...)]
              [(e0) #'(let ([t e0]) (if t t))]
              [(e0 => e1) #'(let ([t e0]) (if t (e1 t)))]
              [(e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...))]
              [_ (syntax-error x)])]
           [(c2 c3 ...)
            (with-syntax ([rest (f #'c2 #'(c3 ...))])
              (syntax-case c1 (else =>)
                [(e0) #'(let ([t e0]) (if t t rest))]
                [(e0 => e1) #'(let ([t e0]) (if t (e1 t) rest))]
                [(e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...) rest)]
                [_ (syntax-error x)]))]))]))

With define-syntax-case (at least if I understand you correctly)
it becomes:

(define-syntax-case cond ()
  (x (syntax-case x ()
      [(_ c1 c2 ...)
       (let f ([c1 #'c1] [c2* #'(c2 ...)])
         (syntax-case c2* ()
           [()
            (syntax-case c1 (else =>)
              [(else e1 e2 ...) #'(begin e1 e2 ...)]
              [(e0) #'(let ([t e0]) (if t t))]
              [(e0 => e1) #'(let ([t e0]) (if t (e1 t)))]
              [(e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...))]
              [_ (syntax-error x)])]
           [(c2 c3 ...)
            (with-syntax ([rest (f #'c2 #'(c3 ...))])
              (syntax-case c1 (else =>)
                [(e0) #'(let ([t e0]) (if t t rest))]
                [(e0 => e1) #'(let ([t e0]) (if t (e1 t) rest))]
                [(e0 e1 e2 ...) #'(if e0 (begin e1 e2 ...) rest)]
                [_ (syntax-error x)]))]))]))

Refering to the
original piece of syntax is often neccessary in order to give
error messages in terms of user written syntax.

I agree with you that the common case should be convenient
to write, in this case I'm not sure I think it is worth
introducing an extra binding form in order to save relatively
few key strokes.

It's not the number of keystrokes that matter, it's the number
of tokens.  Each token adds to the cognitive load required to
read a definition.  A programmer familiar with the idiom can
abstract way the boiler-plate fairly easily, but it is still
an extra required but useless mental step.

Adding an extra binding form also adds to the cognitive load
for those learning the macro system. For the experienced
macro writer it hardly matters which is used.

--
Jens Axel Søgaard