[racket] Macro Help

From: Ryan Culpepper (ryan at cs.utah.edu)
Date: Sun Feb 26 21:07:12 EST 2012

On 02/26/2012 05:18 PM, Helmut Rohrbacher wrote:
> I'm trying to write a macro provide a new way to define functions so that:
>
>
>     (define fold-left
>        (function
>         _ i '() -> i
>         f i (cons x xs) -> (fold-left f (f i x) xs)))
>
>
> Would be valid syntax that would expand to:
>
>     (define fold-left
>        (match-lambda*
>          [(list _ i '()) i]
>          [(list f i (cons x xs)) (fold-left f (f i x) xs)]))
>
> So far I have been able to get there half-way by wrapping each /args
> .../ -> /body/ pattern in parens, which makes the above declaration look
> like this:
>
>     (define fold-left
>        (function
>         [_ i '() -> i]
>         [f i (cons x xs) -> (fold-left f (f i x) xs)]))
>
> By using this macro:
>
>     (define-syntax function
>        (syntax-rules (->)
>          [(_ (args ... -> body) ...)
>           (match-lambda*
>             [(list args ...) body]
>             ...)]))
>
>
> What macro voodoo do I need to harness in order to be able to implement
> the first definition syntax where the /args ... -> body/ segments need
> not be wrapped in parens?

IIUC, the syntax you want for 'function' could be described as follows:

   (function clause ...)
   where clause = arg-pattern ... -> body-expr
         arg-pattern is any match-pattern other than a literal '->'

You can express nonterminals like 'clause' using syntax-parse's splicing 
syntax classes:

(require (for-syntax syntax/parse))

(begin-for-syntax

   ;; clause = arg-pattern ... -> body-expr
   (define-splicing-syntax-class clause
     (pattern (~seq arg:argument-pattern ... (~literal ->) body:expr)))

   ;; arg-pattern is any match-pattern other than a literal '->'
   ;; just accept any term other than '->'
   (define-syntax-class argument-pattern
     (pattern (~not (~literal ->)))))

The '~seq' means "several terms in sequence but not parenthesized".

Given those definitions, you can write the macro as follows:

(define-syntax (function stx)
   (syntax-parse stx
     [(_ f:clause ...)
      #'(match-lambda*
          [(list f.arg ...) f.body]
          ...)]))

The pattern variable 'f' is declared to have the (splicing) syntax class 
'clause', so you can use the attributes of 'clause' (which are 'arg' and 
'body') by writing 'f.arg' and 'f.body'. See the syntax-parse docs for 
more details.

Ryan

Posted on the users mailing list.