# Racket macro: cond/define

**URL:** <https://racket.discourse.group/t/racket-macro-cond-define/3053>\
**Category:** Show & Tell\
**Tags:** macro, guide, blog-post\
**Created:** [July 28, 2024, 2:58pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053 "2024-07-28T14:58:53Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![cadence](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/cadence/32/998_2.png) [@cadence](https://racket.discourse.group/u/cadence)\
**Post date:** [July 28, 2024, 2:58pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/1 "2024-07-28T14:58:53Z")

</div>

I wrote a macro and wrote a blog post about it: [Racket macro: cond/define - cadence's weblog (personal blog)](https://cadence.moe/blog/2024-07-28-racket-macro-cond-define)

* * *

Here's the macro:

```scheme
(define-syntax (cond/define stx)
  (syntax-parse stx
    ;; terminating with else
    [(_ [(~datum else) expr ...])
     #'(begin expr ...)]
    ;; continuing with define
    [(_ ((~datum define) name expr) cond ...)
     #'(let ([name expr])
         (cond/define cond ...))]
    ;; continuing with a condition
    [(_ [condition expr ...] cond ...)
     #'(if condition
           (begin expr ...)
           (cond/define cond ...))]))

```

You are welcome to use, reproduce and modify this macro without restriction. Attribution is optional.

My blog post also talks about how the macro works. This is not the most well-explained macro tutorial on the web, but it is a tutorial I wish I had seen a few months ago when I was just barely dipping my toes into syntax-parse. In particular, my post shows how to use `~datum`, `...`, recursive macros, and describes the inscrutable error if you forget to `(require (for-syntax racket/base syntax/parse))`, which are all things I _really_ struggled with initially.

In the future I might write another similar post where I show how and why to use `#:with`, macros in Typed Racket, and [`#,@#'`](https://github.com/search?q=%23%2C%40%23%27&type=code&repo=&langOverride=&start_value=1).

---

<div class="post-metadata">

**Author:** ![benknoble](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/benknoble/32/16_2.png) [@benknoble](https://racket.discourse.group/u/benknoble)\
**Post date:** [July 28, 2024, 3:31pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/2 "2024-07-28T15:31:51Z")

</div>

A few ideas that occur to me:

- given the prevalence of macros like this, should Rhombus's multi-arm conditional forms allow intermediate defines?
- You might want to use `(let () expr ...)` for your clauses to permit internal definitions (try `(cond/define [else (define x 1) (add1 x)])` and compare with Racket's `cond`).
- The pattern matching on define and else could be `~literal`, since Racket has bindings for both.
- You might want to allow empty `(cond/define)` as your base case so that `(cond/define [1 2])` and similar from Racket work.

---

<div class="post-metadata">

**Author:** ![damien\_mattei](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/damien_mattei/32/2706_2.png) [@damien\_mattei](https://racket.discourse.group/u/damien_mattei)\
**Post date:** [July 29, 2024, 12:19pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/3 "2024-07-29T12:19:36Z")

</div>

It is a good idea. I had written such a macro that is integrated in Scheme+ (cf. 1), the macro is 'condx' named because it allows eXecution of code between each 'cond' clause.

Here is the code of my macro:

```scheme
(define-syntax condx
  (syntax-rules (exec else)
    ((_) '()) ;; allow no else clause
    ((_ (else e ...))
     (let () e ...))
    ((_ (exec s ...) d1 ...)
     (let () s ... (condx d1 ...)))
    ((_ (t e ...) tail ...)
     (if t
         (let () e ...)
         (condx tail ...)))))

```

note that it did not use syntax features because i did not knew too much about that at the time i wrote it, also i wanted to be compatible with Scheme, notonly Racket.

and a running example is:

```scheme
(define x 1)
(condx ((= x 7) 'never)

       (exec
          (define y 3)
          (set! x 7))

       ((= y 1) 'definitely_not)

       (exec
          (set! y 10)
          (define z 2))

       ((= x 7) (+ x y z))
        
       (else 'you_should_not_be_here))

 19 

```

condx is a bit different of cond/define but they both have the same goal: make code easier to read, avoid nested cond or let .

Another solution to avoid nested 'cond' and 'if' is to have some 'return' features from a Scheme procedure.

This is for the reason i developped 'return' and return recursively 'return-rec' and the 'def' macro for defining procedures using 'return' or 'return-rec'.

Then you can do all the coding only with 'if' and 'return' without using 'cond', explanation can be found on this page (2) and examples too.

References:

1 : [Scheme-PLUS-for-Racket/src/condx.scm at main · damien-mattei/Scheme-PLUS-for-Racket · GitHub](https://github.com/damien-mattei/Scheme-PLUS-for-Racket/blob/main/src/condx.scm)

> <https://github.com/damien-mattei/Scheme-PLUS-for-Guile/blob/main/condx.scm>

> <https://github.com/damien-mattei/Scheme-PLUS-for-Kawa/blob/main/condx.scm>

2: [[“Scheme+ programing language by Damien Mattei”] | [“Enhancing Scheme (and Lisp-like) languages.”]](https://damien-mattei.github.io/Scheme-PLUS-for-Racket/Scheme+io.html#condx)

[[“Scheme+ programing language by Damien Mattei”] | [“Enhancing Scheme (and Lisp-like) languages.”]](https://damien-mattei.github.io/Scheme-PLUS-for-Racket/Scheme+io.html#return)

---

<div class="post-metadata">

**Author:** ![hendrikboom3](https://avatars.discourse-cdn.com/v4/letter/h/b5e925/32.png) [@hendrikboom3](https://racket.discourse.group/u/hendrikboom3)\
**Post date:** [July 29, 2024, 8:12pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/4 "2024-07-29T20:12:42Z")

</div>

The package parendown is another (and more general) way of eliminating the tail-recursive  
nesting that otherwise would indent your code off the page.

[https://docs.racket-lang.org/parendown/index.html](https://docs.racket-lang.org/parendown/index.html)

-- hendrik

---

<div class="post-metadata">

**Author:** ![cadence](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/cadence/32/998_2.png) [@cadence](https://racket.discourse.group/u/cadence)\
**Post date:** [July 29, 2024, 8:44pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/5 "2024-07-29T20:44:13Z")

</div>

> [@damien\_mattei](#):
>
> Another solution to avoid nested 'cond' and 'if' is to have some 'return' features from a Scheme procedure.

Yes, early return would definitely avoid needing workarounds like these.

---

<div class="post-metadata">

**Author:** ![usao](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/usao/32/1375_2.png) [@usao](https://racket.discourse.group/u/usao)\
**Post date:** [July 31, 2024, 9:35am UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/6 "2024-07-31T09:35:37Z")

</div>

> [G]iven the prevalence of macros like this, should Rhombus's multi-arm conditional forms allow intermediate defines?

I’m not sure what that should look like, since we don’t allow arbitrary groups among alts. Putting the intermediate groups inside an alt makes the (apparent) scoping quite confusing. (And intermediate definitions _among_ branches really aren’t usual at all.)

---

<div class="post-metadata">

**Author:** ![hendrikboom3](https://avatars.discourse-cdn.com/v4/letter/h/b5e925/32.png) [@hendrikboom3](https://racket.discourse.group/u/hendrikboom3)\
**Post date:** [August 1, 2024, 7:16pm UTC](https://racket.discourse.group/t/racket-macro-cond-define/3053/7 "2024-08-01T19:16:04Z")

</div>

And mechanisms like parendown can eliminate the deep nesting that might occur.  
I like it because it's a much more general mechanism than these macros.

-- hendrik
