Hello ! I am messing around with racket macros and I have a question regarding macros generating macros. I originally created a simple macro that creates a local binding which allows rackets define
syntax and limits the arity of functions using the def
syntax-class. Below is the code:
#lang racket
(require racket/local syntax/parse/define (for-syntax syntax/parse racket/list))
(begin-for-syntax
(define-syntax-class racket-define
#:literals (define)
(pattern (define n:id e:expr))
(pattern (define (n:id params:id ...) body:expr ...+)))
(define-syntax-class (local-func arity)
(pattern (def (name:id params:id ...) body:expr ...+)
#:with arg-len (length (syntax->list #'(params ...)))
#:fail-when (not (equal? (syntax-e #'arg-len) arity))
(format "Arity mismatch. Expected ~s, but was given ~s" arity (syntax-e #'arg-len)))))
(define-syntax (local-arity-1 stx)
(syntax-parse stx
[(_ [(~or func:racket-define (~var func (local-func 1))) ...+] body)
#:fail-when (empty? (syntax->list #`((~? func.name) ...)))
"Expected at least one 'def' clause"
#`(local
((~? (define (func.name func.params ...) func.body ...)) ...) body)]
[(_) (raise-syntax-error #f "Expected at least one 'def' clause" stx)]))
(local-arity-1
[(define x 10)
(define (add x y) (+ x y))
(def (add1 x) (+ 1 x))
(def (sub x y) (- x y)) ;; errors because only one param is allowed
]
(add1 20))
I now would like to take this a step further and create a macro that will generate the above macro when given a name and a number representing the arity for the def
. Currently I have to following:
(define-syntax-parse-rule (generate-local-arity macro-name:id num:expr)
(define-syntax (macro-name stx)
(syntax-parse stx
[(_ [(~or func:racket-define (~var func (local-func num))) (... ...+)] body)
#:fail-when (empty? (syntax->list #`((~? func.name) (... ...))))
"Expected at least one 'def' clause"
#`(local
((~? (define (func.name func.params (... ...)) func.body (... ...))) (... ...)) body)]
[(_) (raise-syntax-error #f "Expected at least one 'def' clause" stx)])))
(generate-local-arity local-arity-3 3)
(local-arity-3
[(define x 10)
(define (add x y) (+ x y))
(def (test x y z) (+ x y z))]
(test 10 10 10))
The above works fine when only the def
syntax class is used, however once I add the define
syntax-class I get to following error: func.name: attribute contains non-syntax value value: #f in: func.name #(1418 9)
. When I ran the code thought the macro stepper I noticed that ~?
is being removed from the generated code which explains why the above error is being outputted. Is there a special syntax that I need to use for nested head patterns?
Any help is appreciated!
-Josh