Question regarding head patterns in macros generating macros

Hello :wave:! 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

1 Like

It’s likely because ~? is associated with the level of the outer macro (generate-local-arity), not the inner macro (macro-name).

One easy solution is to just escape the entire thing. This is viable in your case since generate-local-arity doesn’t have any ... in the pattern.

(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)]))))
3 Likes