defining macro-generated macros that take in variable number of arguments
Asked Answered
N

2

6

I am trying to write a macro-generating macro, where the macro it generates takes a variable number of arguments.

I am wondering if there is a way to make the following code work:

(define-syntax-rule (greet name)
  (define-syntax-rule (name args ...)
    (printf "hello ~a~n" (list args ...))))

Right now it says "no pattern variables before ellipsis in template in: ..."

If I take the inner define-syntax-rule by itself it works fine, so why doesn't it work when it's being generated by another macro?

Nibbs answered 20/12, 2018 at 6:41 Comment(2)
One important element to this question is that you're defining a macro-generating macro, where the generated macro takes a variable number of arguments. If you just write the inner define-syntax-rule directly, there's no problem. It's only putting that inside another outer define-syntax-rule that causes the problem.Demerit
This question may be related: Capturing a variable number of arguments via an ellipsis in a nested macro; Missing pattern variable errorDemerit
D
8

There are at least 3 "Styles" of doing this.

1: Ellipsis-quoting each ellipsis separately

Soegaard already answered that you can replace every ... in the body with (... ...), so that it gets interpreted as a literal ellipsis belonging to the inner macro instead of as a "meta" ellipsis belonging to the outer macro:

(define-syntax-rule (greet name)
  (define-syntax-rule (name args (... ...))
    (printf "hello ~a~n" (list args (... ...)))))

Advantages: Flexible, you can mix literal (... ...) and meta ... ellipses freely within the body

Disadvantages: Looks confusing if you haven't seen (... ...) before

2: Ellipsis-quoting the whole inner macro definition

However, putting (... <something>) around something is not limited to just .... If you put a whole template there, any ...s within that template will also be "quoted", treated as literal instead of meta, in the same way:

(define-syntax-rule (greet name)
  (...
   (define-syntax-rule (name args ...)
     (printf "hello ~a~n" (list args ...)))))

Advantages: If you have even greater nesting depths you wouldn't need ((... ...) (... ...)) as you would with option 1, you would just need (... <something-containing (... <something>)>)

Disadvantages: Rigid, if you put (... <something>) around something you can't ever use a meta ellipsis inside that something. You can't mix literal and meta ellipses freely like you could with style 1 or 3.

3: Creating a pattern-variable to represent a literal ellipsis

Here's another way, which I find less confusing, but it requires using define-simple-macro instead of define-syntax-rule, so that you can bind new pattern variables using #:with.

(require syntax/parse/define)

(define-simple-macro (<name> <arguments>)
  #:with <pattern-variable> <expression>
  <body-expression>)

You can use with #:with to bind an ooo pattern variable to a literal ellipsis: #:with ooo (quote-syntax ...)

(require syntax/parse/define)

(define-simple-macro (greet name)
  #:with ooo (quote-syntax ...)
  (define-syntax-rule (name args ooo)
    (printf "hello ~a~n" (list args ooo))))

Advantages: Flexible, you can mix literal ooo and meta ... ellipses freely within the body. To me, it looks less confusing than (... ...) or ((... ...) (... ...)).

Disadvantages: For deeper nesting, you might need multiple #:with-definitions, one on each meta-level.

Demerit answered 20/12, 2018 at 20:17 Comment(0)
B
5

The ... belongs to the outer define-syntax-rule. In order to produce an ellipsis in the output you need to quote it with (... ...).

(define-syntax-rule (greet name)
  (define-syntax-rule (name args (... ...))
    (printf "hello ~a~n" (list args (... ...)))))

For fun: If you ever need to write a macro that produces a macro that produces a macro, you will need ((... ...) (... ...)) to produce an ellipsis.

Broderick answered 20/12, 2018 at 10:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.