# Macro Template Syntax Quasi-Quote with Ellipses

**URL:** <https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179>\
**Category:** Questions & Answers\
**Tags:** question, macro\
**Created:** [July 25, 2022, 4:04am UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179 "2022-07-25T04:04:12Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Liam.Gaudy](https://avatars.discourse-cdn.com/v4/letter/l/a6a055/32.png) [@Liam.Gaudy](https://racket.discourse.group/u/Liam.Gaudy)\
**Post date:** [July 25, 2022, 4:04am UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/1 "2022-07-25T04:04:12Z")

</div>

I'm using the below as an example to ask the question. Please recall that [case](https://docs.racket-lang.org/reference/case.html) works like this:

```scheme
(case (+ 7 5)
   [( 1 2 3) 'small]
   [( 10 11 12) 'big])

```

Let's say I never need more than one match for the same body. So, I write a macro to allow dropping the extra () around all the datum clauses (labeled num below):

```scheme
(define-syntax-rule (my-case check [num body] ...)
  (case check [( num) body] ...))

```

This way I can just say:

```scheme
(my-case check -> (case check
  [1 (action1)] -> [( 1) (action1)]
  [5 (action5)]) -> [( 5) (action5)])

```

And the macro will put-in the missing () for the correct use of case. But what if I want the template to be conditionally built on the type of num either being: 1) a [number?](https://docs.racket-lang.org/reference/number-types.html#%28def._%28%28quote._~23~25kernel%29._number~3f%29%29) to behave as above, or being: 2) a [list?](https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._list~3f%29%29) to behave the normal way [case](https://docs.racket-lang.org/reference/case.html) works, like this:

```scheme
(my-case check -> (case check
  [1 (action1)] -> [( 1) (action1)]
  [5 (action5)] -> [( 5) (action5)]
  [( 2 4 6) (action-special)) -> [( 2 4 5) (action-special)])

```

What can I do to the code below to make it work for more clauses? Where should the [ellipsis ...](https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._......%29%29) go?

```scheme
(define-syntax (my-case stx)
  (syntax-case stx ()
    [(_ check [num body] ) ;like this?: body] ... ) this gives this error:
     #` (case check ;missing ellipsis with pattern variable in template
         [#,(if (number? (syntax-e #'num))
                (list #'num)
                #'num)
          body])]))

```

I feel I'm missing something basic. How can I conditionally build a template while unravelling the ellipsis into the parent template accordingly? Maybe I'm approaching it wrong? Any advise would be appreciated.

---

<div class="post-metadata">

**Author:** ![dominik.pantucek](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/dominik.pantucek/32/144_2.png) [@dominik.pantucek](https://racket.discourse.group/u/dominik.pantucek)\
**Post date:** [July 25, 2022, 7:24am UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/2 "2022-07-25T07:24:15Z")

</div>

If I am not mistaken:

```scheme
(define-syntax (my-case stx)
  (syntax-case stx ()
    [(_ check [num body ...] ...)
     (with-syntax (((the-num ...)
                    (for/list ((num1 (syntax->list #'(num ...))))
                      (if (not (list? (syntax->datum num1)))
                          (list num1)
                          num1))))
       #'(case check
           [the-num
            body ...] ...))]))

```

Sorry for the weird identifier names, of course it evolved from my original "just use double ellipsis" a bit 😉

---

<div class="post-metadata">

**Author:** ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)\
**Post date:** [July 25, 2022, 12:15pm UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/3 "2022-07-25T12:15:08Z")

</div>

There are several possible approaches. Below I will use the `syntax/parse` library, but you should be able to translate them to vanilla Racket constructs — `syntax-case` / `define-syntax-rule` / `syntax-rules` — except when indicated otherwise.

1. syntax class: this is what I would personally do. It can’t be easily translated to vanilla Racket because syntax class is a concept introduced in `syntax/parse`.

```scheme
#lang racket

(require syntax/parse/define)

(begin-for-syntax
  (define-syntax-class lhs
    (pattern x:number #:with out #'(x))
    (pattern (y:number ...) #:with out #'(y ...))))

(define-syntax-parse-rule (my-case v [l:lhs r ...+] ...)
  (case v [l.out r ...] ...))

```

1. recursive expansion: this is a pure term rewriting, with no escape back to Racket

```scheme
#lang racket

(require syntax/parse/define)

(define-syntax-parse-rule (my-case v [lhs body ...+] ...)
  (my-case* v
            ([lhs body ...] ...)
            ())) ;; this is an "accumulator"

(define-syntax-parser my-case*
  ;; no more clause to process
  [(_ v () ([lhs . body] ...))
   #'(case v [lhs . body] ...)]

  ;; process each clause one-by-one
  [(_ v ([x:number . body] rst ...) (out ...))
   #'(my-case* v (rst ...) (out ... [(x) . body]))]
  [(_ v ([(x:number ...) . body] rst ...) (out ...))
   ;; this puts stuff at the end of the list, which is inefficient if there are a lot of clauses
   #'(my-case* v (rst ...) (out ... [(x ...) . body]))])

```

1. `#:with` / `with-syntax`: this is essentially @dominik.pantucek’s solution

```scheme
#lang racket

(require syntax/parse/define)

(define-syntax-parse-rule (my-case v [num body ...+] ...)
  #:with (new-num ...)
  (map (λ (n)
         (if (list? (syntax-e n))
             n
             (list n)))
       (attribute num))

  (case v [new-num body ...] ...))

```

---

<div class="post-metadata">

**Author:** ![Liam.Gaudy](https://avatars.discourse-cdn.com/v4/letter/l/a6a055/32.png) [@Liam.Gaudy](https://racket.discourse.group/u/Liam.Gaudy)\
**Post date:** [July 25, 2022, 1:30pm UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/4 "2022-07-25T13:30:08Z")

</div>

Thank you @dominik.pantucek. I know what you mean. Sometimes our intuition just works its magic. Yours was very clear. To recap, for my understanding:

1. Using [with-syntax](https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._with-syntax%29%29), which is a kind of [let](https://docs.racket-lang.org/reference/let.html#%28form._%28%28lib._racket%2Fprivate%2Fletstx-scheme..rkt%29._let%29%29) for patterns, conveniently takes ellipsis. This allows taking apart a sub-pattern, called pick below.
2. This allows us to go through a list in the main pattern to examine how (and most importantly if) it should go into our picked sub-pattern.
3. Then very easily, in a normal #'block, we can just put back our sub-pattern that we just built.

```scheme
(define-syntax (my-case stx)
  (syntax-case stx ()
    [(_ check [num body ...] ...)
     (with-syntax ([( pick ...)
                    (for/list ([head (in-list (syntax->list #'(num ...)))])
                      (if (not (list? (syntax->datum head)))
                          (list head)
                          head))])
       #'(case check
           [pick body ...] ...))]))

```

Thank you @sorawee.

1. Here a syntax-class was used to recognize and name some sub-patterns using a notation that loads semantics inside token names as the spec for the syntax-class.
2. Here a common recursive accumulation technique was used, but with a syntax-parser that takes sub-patterns then regularize them into/using an internal helper.
3. Here apparently a syntax-parse-rule can have a directive for making a pattern-let that takes a function to fill it from on a specific sub-part in the main pattern.

Since you agree with @dominik.pantucek on the last one, I'll pick his version since it's in pure Racket. This tour was wonderful and gave me a sense for how robust the syntax-parse library approaches the subject. Thanks for taking the time!

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [July 25, 2022, 8:39pm UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/5 "2022-07-25T20:39:26Z")

</div>

Another option would be to use something other than `case`, with `match` being the obvious choice. For example:

```scheme
(match check
  [1 (action1)]
  [5 (action5)])

```

`case` is intended to be used when you have multiple items that might trigger the action. If there's only one then you're likely better off with `match`.

---

<div class="post-metadata">

**Author:** ![Liam.Gaudy](https://avatars.discourse-cdn.com/v4/letter/l/a6a055/32.png) [@Liam.Gaudy](https://racket.discourse.group/u/Liam.Gaudy)\
**Post date:** [July 25, 2022, 10:57pm UTC](https://racket.discourse.group/t/macro-template-syntax-quasi-quote-with-ellipses/1179/6 "2022-07-25T22:57:33Z")

</div>

Thanks, I was just using that as an example to ask a specialized macro question. My understanding of syntax-unquote with ellipsis needed some guidance towards the natural common usage. I take your point though. Sometimes the answer is not to bend a tool for the usage (like we can with macros), but to use the right tool in the first place. Note that `case` has a magical dispatch [research paper](https://github.com/racket/racket/blob/448b77a6629c68659e1360fbe9f9e1ecea078f9c/racket/collects/racket/private/case.rkt) behind it with multiple strategies, while `match` is a specialized tool that checks its patterns in sequence per the [documentation](https://docs.racket-lang.org/reference/match.html).
