[racket] Macro Help
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