# Macro Output Spliced into Context

**URL:** <https://racket.discourse.group/t/macro-output-spliced-into-context/1641>\
**Category:** Questions & Answers\
**Tags:** question, macro\
**Created:** [January 16, 2023, 11:53pm UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641 "2023-01-16T23:53:20Z")\
**Posts on this page:** 10\
**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:** [January 16, 2023, 11:53pm UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/1 "2023-01-16T23:53:21Z")

</div>

I was just about to give up on doing the below, when I thought it might be fun for a more experienced person to offer advice. I have this repetitive `cond` for a comparator, with my thanks to @notjack for writing such important data structures for us all in [rebellion](https://docs.racket-lang.org/rebellion/index.html).

```scheme
(cond
  [(and (null? left-scope) (null? right-scope)) equivalent]
  [(and (null? left-scope) (not (null? right-scope))) lesser]
  [(and (not (null? left-scope)) (null? right-scope)) greater]
  [(< left-scope right-scope) lesser]
  [(> left-scope right-scope) greater]
  [(and (null? left-tag) (null? right-tag)) equivalent]
  [(and (null? left-tag) (not (null? right-tag))) lesser]
  [(and (not (null? left-tag)) (null? right-tag)) greater]
  [(string<? left-tag right-tag) lesser]
  [(string>? left-tag right-tag) greater]
  [else equivalent])))

```

To reduce eye strain and improve readability, I thought I could express it this way instead:

```scheme
(bulk-cond
  (null-pick left-scope right-scope)
  (strict-pick left-scope right-scope < >)
  (null-pick left-tag right-tag)
  (strict-pick left-tag right-tag string<? string>?)
  (else equivalent))

```

I understand this would be an academic exercise, but Racket came from academia; I should be among friends on wanting to do this. So, I went to work with a one-shot approach:

```scheme
(define-syntax bulk-cond
  (syntax-rules (null-pick strict-pick)
    [(_ (null-pick left right) more ...)
     (bulk-cond
      [(and (null? left) (null? right)) equivalent]
      [(and (null? left) (not (null? right))) lesser]
      [(and (not (null? left)) (null? right)) greater]
      more ...)]
    [(_ (strict-pick left right on< on>) more ...)
     (bulk-cond
      [(on< left right) lesser]
      [(on> left right) greater]
      more ...)]
    [(_ more ...)
     (cond more ...)]))

```

But I had a flaw in my recursion that I could not bridge, so I through let me build the constituent parts:

```scheme
(define-syntax-rule (null-pick left right)
  (list ;will need to splice this later
   [(and (null? left) (null? right)) equivalent]
   [(and (null? left) (not (null? right))) lesser]
   [(and (not (null? left)) (null? right)) greater]))

(define-syntax-rule (strict-pick left right on< on>)
  (list ;will need to splice this later
   [(on< left right) lesser]
   [(on> left right) greater]))

```

But I could not stitch the two patterns together correctly. I know the below "cure" is worse than the ailment and is also wrong, but maybe you can offer ideas on how to proceed differently?

```scheme
;wrap the above 2 macros in begin-for-syntax, with:
;(require (for-syntax rebellion/base/comparator)), then:
(define-syntax (bulk-cond stx)
  (syntax-case stx ()
    [(_ pre-chunks ...)
     (with-syntax ([(post-chunks ...)
                    (for/list ([head (in-list (syntax->list #'(pre-chunks ...)))])
                      (if (memq (syntax-e head) '(null-pick strict-pick))
                        ??? ;some kind of spliced call into sub-cases
                        ???))]) ;leave the else case alone
       #'(cond post-chunks ...))]))

```

Or maybe something simpler, where I just express what I need correctly instead of doing it:

```scheme
(define-syntax (bulk-cond stx)
  (syntax-case stx (null-pick strict-pick else)
    [(_ (null-pick n-rest ...) ... (strict-pick s-rest ...) ... o-rest ...)
     #`(cond #,@(null-pick n-rest ...) ...
             #,@(strict-pick s-rest ...) ...
             o-rest ...)]))
;I know this is even more wrong, but I'm flailing at this point

```

Would you kindly attempt a suggestion in pure Racket? I barley have a handle on what I know that I don't think I can expand to syntax-parse or other libraries, unless you really think it would be more straightforward. Anyway, thanks for reading so far.

---

<div class="post-metadata">

**Author:** ![notjack](https://avatars.discourse-cdn.com/v4/letter/n/e47774/32.png) [@notjack](https://racket.discourse.group/u/notjack)\
**Post date:** [January 17, 2023, 12:50am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/2 "2023-01-17T00:50:10Z")

</div>

I believe [`comparator-chain`](https://docs.racket-lang.org/rebellion/Comparators.html#%28def._%28%28lib._rebellion%2Fbase%2Fcomparator..rkt%29._comparator-chain%29%29) can help you here:

```scheme
(define null<=>
  (comparator-map
    (comparator-of-constants 'null 'not-null)
    (lambda (v) (if (null? v) 'null 'not-null))))

(struct data (scope tag) #:transparent)

(define data<=>
  (comparator-chain
    (comparator-map null<=> data-scope)
    (comparator-map real<=> data-scope)
    (comparator-map null<=> data-tag)
    (comparator-map string<=> data-tag)))

(compare data<=> (data left-scope left-tag) (data right-scope right-tag))

```

Edit: fixed a bug where I forgot an `if` in the definition of `null<=>`.

---

<div class="post-metadata">

**Author:** ![notjack](https://avatars.discourse-cdn.com/v4/letter/n/e47774/32.png) [@notjack](https://racket.discourse.group/u/notjack)\
**Post date:** [January 17, 2023, 2:06am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/3 "2023-01-17T02:06:41Z")

</div>

Actually, that didn't quite work the way I hoped: `(compare data<=> (data '() "apple") (data '() "banana"))` throws an exception because it tries to compare `'()` and `'()` with `real<=>`. To fix that, I made this:

```scheme
(define (nulls-first-comparator cmp)
  (make-comparator
   (λ (left right)
     (cond
       [(and (null? left) (null? right)) equivalent]
       [(null? left) lesser]
       [(null? right) greater]
       [else (compare cmp left right)]))))

(struct data (scope tag) #:transparent)

(define data<=>
  (comparator-chain
    (comparator-map (nulls-first-comparator real<=>) data-scope)
    (comparator-map (nulls-first-comparator string<=>) data-tag)))

(compare data<=> (data left-scope left-tag) (data right-scope right-tag))

```

---

<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:** [January 17, 2023, 2:31am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/4 "2023-01-17T02:31:51Z")

</div>

Thank you @notjack for this and for being a great Racketeer that made me feel welcome in this community just by virtue of all the care you put into your libraries, documentation, and presentations. I'm a big fan of yours, especially your ambitious work on [resyntax](https://docs.racket-lang.org/resyntax/index.html). Please don't mind me; I feel this way about most people that contributed to Racket as much as you did.

Your most recent change passed my unit tests for every case. Thanks for steering me in the right direction! I did not need macros at all, after spending a whole day trying to write the right one 🙂.

Just because it might be fun for you to know what I'm using your code for, I'm about to use a sorted-map with a key that is a pair, and trying to supply a comparator for it. The pair is of an immutable scope reference (for a point in somebody's program), and a variable that is declared in that scope, or null. I'm trying to do something related to the genesis of `resyntax` as narrated in the last RacketCon presentation on the subject.

Our interests might be similar, except for my background being a business analyst who oversees the quality of work of other business analysts, and constantly thinking about how to do that systematically.

Here, I plan on filling the sorted-map with a pass of what was declared where so that it is only a matter of querying that data structure to find things like who called what, what is being called by whom, and the like. Not sure if knowing this interests you, but I'm sharing in case it does, because I'd enjoy discussing your side of it, if you like.

In any case, thanks again for your help and publishing very elegant code in our favorite language Racket.

---

<div class="post-metadata">

**Author:** ![notjack](https://avatars.discourse-cdn.com/v4/letter/n/e47774/32.png) [@notjack](https://racket.discourse.group/u/notjack)\
**Post date:** [January 17, 2023, 2:41am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/5 "2023-01-17T02:41:58Z")

</div>

That sounds interesting; I'd be happy to hear more. And I'm glad to know you've gotten some use out of the things I've made 🙂

---

<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:** [January 17, 2023, 3:34am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/6 "2023-01-17T03:34:49Z")

</div>

As for why the following code doesn’t work.

```scheme
(define-syntax bulk-cond
  (syntax-rules (null-pick strict-pick)
    [(_ (null-pick left right) more ...)
     (bulk-cond
      [(and (null? left) (null? right)) equivalent]
      [(and (null? left) (not (null? right))) lesser]
      [(and (not (null? left)) (null? right)) greater]
      more ...)]
    [(_ (strict-pick left right on< on>) more ...)
     (bulk-cond
      [(on< left right) lesser]
      [(on> left right) greater]
      more ...)]
    [(_ more ...)
     (cond more ...)]))

```

The way you wrote `bulk-cond` is that it compiles _only_ the first clause. Therefore, you need a way to “advance” the compilation to the rest of the clauses in the recursive expansion.

One possibility is to use `cond` directly on the compiled clauses.

```scheme
(define-syntax bulk-cond
  (syntax-rules (null-pick strict-pick)
    [(_ (null-pick left right) more ...)
     (cond
       [(and (null? left) (null? right)) 'equivalent]
       [(and (null? left) (not (null? right))) 'lesser]
       [(and (not (null? left)) (null? right)) 'greater]
       [else
        (bulk-cond more ...)])]
    [(_ (strict-pick left right on< on>) more ...)
     (cond
       [(on< left right) 'lesser]
       [(on> left right) 'greater]
       [else
        (bulk-cond more ...)])]
    [(_ more ...)
     (cond more ...)]))

```

Another possibility is to advance it through the last clause.

```scheme
(define-syntax bulk-cond
  (syntax-rules (null-pick strict-pick else)
    [(_ (null-pick left right) more ...)
     (bulk-cond
      [(and (null? left) (null? right)) 'equivalent]
      [(and (null? left) (not (null? right))) 'lesser]
      [(and (not (null? left)) (null? right)) 'greater]
      more ...)]
    [(_ (strict-pick left right on< on>) more ...)
     (bulk-cond
      [(on< left right) 'lesser]
      [(on> left right) 'greater]
      more ...)]
    [(_ (else expr ...))
     (let () expr ...)]
    [(_ more1 more ...)
     (cond
       more1
       [else (bulk-cond more ...)])]))

```

---

<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:** [January 17, 2023, 4:17am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/7 "2023-01-17T04:17:53Z")

</div>

Hello @sorawee, I think you're famous 😄. I now understand both approaches you provided. Thanks for showing me how to do this correctly. Isn't there a typical approach to compose groups of statements into a larger context that is typically used from your experience?

I'm very sure I don't have the right exposure to these idioms to think to use them, because I struggled when searching for examples, but am I correct in that assumption? Macros flow nicely and elegantly when they are transformed from template to template, not when constructed statement by statement, like I was trying to do, right?

---

<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:** [January 17, 2023, 5:00am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/8 "2023-01-17T05:00:23Z")

</div>

There is a way to do “splicing”, but it doesn’t take macro outputs. Instead, it takes syntax templates.

You can’t manipulate syntax templates in `syntax-rules` and `define-syntax-rule`, because these forms don't allow you to do any computation. You are limited to producing a template right away.

`syntax-case` + `with-syntax` could work, but an easier approach would be to use the `syntax/parse` library, which allows you to directly “build the constituent parts” via `define-syntax-class`. Then you can use `~@` for splicing.

```scheme
(begin-for-syntax
  (define-syntax-class clause
    (pattern ({~literal null-pick} left right)
      #:with expanded
      #'([(and (null? left) (null? right)) 'equivalent]
         [(and (null? left) (not (null? right))) 'lesser]
         [(and (not (null? left)) (null? right)) 'greater]))
    (pattern ({~literal strict-pick} left right on< on>)
      #:with expanded
      #'([(on< left right) 'lesser]
         [(on> left right) 'greater]))
    (pattern ({~literal else} expr ...)
      #:with expanded
      #'([else expr ...]))))

(define-syntax-parse-rule (bulk-cond c:clause ...)
  (cond {~@ . c.expanded} ...))

```

---

<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:** [January 17, 2023, 6:15am UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/9 "2023-01-17T06:15:10Z")

</div>

Wow, that is very elegant, it flows so naturally, easy to read and reason about. Maybe I shouldn't be allergic so much to syntax/parse. It just feels like a luxury to know how to use it. Thanks again, you're a treasure to us all. I'll be referring to this from to time. One day I hope to collect many of Racket's common idioms in some centralized place in a format easy to lookup and play with for casual programmers that enjoy this stuff like myself.

---

<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:** [January 17, 2023, 5:13pm UTC](https://racket.discourse.group/t/macro-output-spliced-into-context/1641/10 "2023-01-17T17:13:07Z")

</div>

I'm marking this as the answer. First, we have a great and thoughtful Racket community, full of kind and supportive leaders that I'm grateful for. Second, sometimes the answer is not a macro, but composability in functions. Same abstraction work in breaking apart and bringing together, just in terms of compiled functionality. Finally, if you really want to do unusual stuff, try `syntax/parse`. Don't be afraid of it being considered advanced and having an academic paper backing it, like I did. Most of Racket has such papers.
