<p>This is an example for the use of Rust's <code>macro_rules!</code>:</p>
<pre class="lang-rust prettyprint-override"><code>macro_rules! add {
    ($a:expr,$b:expr) =&gt; {{
        $a + $b
    }};
    ($a:expr) =&gt; {{
        $a
    }};
}
</code></pre>
<p>A similar example for Scheme using <code>syntax-rules</code> could be:</p>
<pre class="lang-scheme prettyprint-override"><code>(define-syntax add
  (syntax-rules ()
    ((_ a b) (+ a b))
    ((_ a)   a)))
</code></pre>
<p>One might think that this two language features are equivalent.  But is it the case or is there a hidden difference?</p>

<rust><lisp><meta-programming><scheme>
Is there a difference between Rust's `macro_rules!` and Scheme's `syntax-rules`?
<p><a href="https://www.reddit.com/r/rust/comments/azlqnj/prior_art_for_rusts_macros/ei8tf2a/" rel="noreferrer">Rust's macros are directly inspired by Scheme's macros</a>, but they aren't the same.</p>
<p>First, I should clarify that while there is a formal specification to Scheme (R<em>n</em>RS), there are many more commonly used extended dialects, and each have their own <code>syntax-rules</code> implementations which are slightly different. Here I cite <a href="https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_chap_12" rel="noreferrer">R6RS</a>, <a href="https://www.scheme.com/csug8/syntax.html" rel="noreferrer">Chez Scheme</a>, <a href="https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html" rel="noreferrer">Guile</a> and <a href="https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._syntax-rules%29%29" rel="noreferrer">Racket</a>. I believe all the similarities and differences listed apply between Rust and any of the Scheme dialects.</p>
<h2>Similarities</h2>
<ul>
<li>All have <strong>function-like syntax</strong>, but unlike functions, are evaluated at compile-time.</li>
<li>All do a sort of <strong>pattern-match on the AST</strong> provided and produce a new AST.</li>
<li>All can have <strong>multiple cases</strong>, which are tried top-to-bottom.</li>
<li>All have some form of <strong>hygiene</strong>, which prevents identifiers inside the macro from mixing with identifiers with the same name outside of the macro (<a href="https://danielkeep.github.io/tlborm/book/mbe-min-hygiene.html" rel="noreferrer">Rust</a>, <a href="https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_sec_12.1" rel="noreferrer">R6RS</a>, <a href="https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html#Hygiene" rel="noreferrer">Guile</a>, <a href="https://beautifulracket.com/explainer/hygiene.html" rel="noreferrer">Racket</a>).</li>
<li>All allow <strong>recursive macros</strong> (macros whose expansion calls themselves again) and <strong>nested macros</strong> (macros whose expansion defines other macros). For nested macros, there are ways to get <a href="https://stackoverflow.com/a/53971532">the <code>$</code> in Rust</a> and <a href="https://stackoverflow.com/a/68743610"><code>...</code> in Scheme</a> within the inner macros working correctly.</li>
<li>All have a long list of &quot;tricks&quot; to create complicated macros (examples in <a href="https://blog.cloudflare.com/writing-complex-macros-in-rust-reverse-polish-notation/" rel="noreferrer">Rust</a>, <a href="https://cohost.org/poliorcetics/post/363003-a-rust-macro-trick-i" rel="noreferrer">2</a>, <a href="https://github.com/mnieper/scheme-macros" rel="noreferrer">Scheme</a>, <a href="https://okmij.org/ftp/Scheme/macros.html" rel="noreferrer">2</a>, <a href="https://www.cs.rice.edu/%7Ejavaplt/311/Readings/macroslides.pdf" rel="noreferrer">3</a>; just Google &quot;Rust macro tricks&quot; and &quot;Scheme macro tricks&quot; for more examples).</li>
</ul>
<h2>Differences</h2>
<ul>
<li><strong>Rust macros may match syntax elements of particular types (e.g. <code>expr</code>)</strong>. These syntax elements may actually consist of multiple syntax-nodes and require further parsing before they get matched (very complicated). <code>syntax-rules</code> only matches individual nodes (equivalent to <code>tt</code>), and matching is done after all parsing (very simple).</li>
<li><strong>Rust has &quot;optional fragments&quot;</strong> (<code>$(...)?</code>) which match zero-or-one repetition of a sequence of syntax-nodes, and <code>$(...)+</code> fragments which match one-or-many. Scheme only has <code>...</code> which matches zero-or-many repetitions of a syntax-node.
<ul>
<li>Related, <strong>Rust's repetitions can contain multiple syntax-nodes</strong> (which are matched in repeating sequence, e.g. <code>$(a b)*</code> matches <code>a b a b a b</code>), Scheme's repetitions only contain one.</li>
<li>Scheme patterns may also have a dotted-pair, which matches the remaining elements of a Scheme &quot;list&quot; including the dotted-pair if it ends in one. Rust has no analogue to dotted-pairs.</li>
</ul>
</li>
<li>Rust has a large set of <a href="https://doc.rust-lang.org/reference/macro-ambiguity.html" rel="noreferrer">ambiguity rules</a> due to its complicated macro parsing and forwards-compatibility with new syntax. <strong>Scheme has no ambiguity rules, because there's no ambiguity to how the macro pattern is matched</strong>, and Scheme's syntax will never change to introduce one.</li>
<li>Macros in Rust can only be local to a module, unless you use <a href="https://github.com/rust-lang/rust/issues/39412" rel="noreferrer">macros 2.0</a> which is experimental and requires nightly Rust. <strong>Scheme has <code>let-syntax</code> which creates its own local scope and defines macros which only apply to that scope</strong> (e.g. <code>(let-syntax ([f (syntax-rules () [(_ x) x])]) (f 1))</code>).</li>
<li>This is technically off-topic because it goes beyond <code>syntax-rules</code> and <code>macro_rules!</code>, but <strong>the way that Rust and Scheme let you write more complicated macros are completely different</strong>.
<ul>
<li>Rust's <code>macro_rules!</code> (AKA &quot;Macro By Example&quot;) is very limited. If you want to &quot;pull back the curtain&quot; and manipulate syntax programmatically within the Rust language, you must use <a href="https://doc.rust-lang.org/reference/procedural-macros.html" rel="noreferrer">procedural macros</a>. These must be defined in a separate <code>proc-macro</code> crate which the code which uses the macros must depend on. Moreover, proc-macros abandon Rust's powerful syntax pattern-matching and give you the raw stream of tokens to manipulate directly.</li>
<li>In contrast, <strong>Scheme has <a href="https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html" rel="noreferrer"><code>syntax-case</code></a> which is what <code>syntax-rules</code> expands to</strong>. <code>syntax-case</code> still lets you pattern-match syntax, but instead of the RHS being the result AST, <strong><code>syntax-case</code> contains arbitrary Scheme code which gets evaluated and produces a <a href="https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_sec_12.2" rel="noreferrer">&quot;syntax object&quot;</a></strong> like <code>#'(foo (bar . baz))</code>. <strong>Macros defined with <code>define-syntax</code>/<code>let-syntax</code> and <code>syntax-case</code> can be used within the same file</strong>, and Racket in particular even lets you define macros for other macros within the same file using statements like <a href="https://docs.racket-lang.org/reference/begin.html#%28form._%28%28quote._%7E23%7E25kernel%29._begin-for-syntax%29%29" rel="noreferrer"><code>begin-for-syntax</code></a> (see <a href="https://docs.racket-lang.org/guide/phases.html" rel="noreferrer">phase levels</a>)</li>
</ul>
</li>
</ul>
<h2><a href="https://docs.racket-lang.org/syntax/stxparse.html" rel="noreferrer"><code>syntax/parse</code></a>: the real origin of Rust-style macros</h2>
<p>Racket in particular has a newer macro system, <code>syntax/parse</code>, which is what specifically inspired Rust's macros. <a href="https://docs.racket-lang.org/syntax/Defining_Simple_Macros.html" rel="noreferrer"><code>define-syntax-parse-rule</code></a> is the analogue to Rust's <code>macro_rules!</code>.</p>
<p>Like <code>macro_rules!</code>, <code>syntax/parse</code> can match different kinds of syntax using <code>:expr</code>, <code>:id</code>, etc. (called <a href="https://docs.racket-lang.org/syntax/Library_Syntax_Classes_and_Literal_Sets.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._expr%29%29" rel="noreferrer">syntax classes</a>, although in Rust they're called <a href="https://doc.rust-lang.org/rust-by-example/macros/designators.html" rel="noreferrer">designators</a>). <code>syntax/parse</code> can also match repeating sequences of nodes using <a href="https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._%7E7eseq%29%29" rel="noreferrer">the <code>~seq</code> syntax pattern</a>.</p>
<p>But <code>syntax/parse</code> is much more expressive than <code>macro_rules!</code>. You can <a href="https://docs.racket-lang.org/syntax/stxparse-specifying.html" rel="noreferrer">define your own syntax classes</a>, and there are other <a href="https://docs.racket-lang.org/syntax/stxparse-patterns.html" rel="noreferrer">syntax patterns</a>; e.g. <a href="https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28elem._%28pattern-link._%28%7E7eor%2A._s%29%29%29" rel="noreferrer"><code>~or</code></a> lets you match local alternatives (e.g. <code>(number (~or 1 2 3))</code> matches <code>(number 1)</code> and <code>(number 3)</code>), and <a href="https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._%7E7enot%29%29" rel="noreferrer"><code>~not</code></a> creates a macro which matches any syntax node <em>except</em> ones which match its argument. Moreover, <code>syntax/parse</code> provides <a href="https://docs.racket-lang.org/syntax/Parsing_Syntax.html" rel="noreferrer"><code>syntax-parse</code></a>, the analogue to <code>syntax-case</code>, which lets you evaluate arbitrary Racket code when producing the result syntax.</p>
<hr />
<h2>Main references</h2>
<ul>
<li><p><a href="https://doc.rust-lang.org/reference/macros-by-example.html" rel="noreferrer">Rust <code>macro_rules!</code> &quot;Macros by Example&quot;</a></p>
</li>
<li><p><a href="https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html" rel="noreferrer">R6RS <code>syntax-case</code></a> (which <code>syntax-rules</code> expands to)</p>
</li>
<li><p><a href="https://www.scheme.com/csug8/syntax.html" rel="noreferrer">Chez Scheme macros</a></p>
</li>
<li><p><a href="https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html" rel="noreferrer">Guile Scheme <code>syntax-rules</code></a></p>
</li>
<li><p><a href="https://docs.racket-lang.org/guide/pattern-macros.html" rel="noreferrer">Racket scheme pattern-based macros</a>, <a href="https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._syntax-rules%29%29" rel="noreferrer"><code>syntax-rules</code></a> and <a href="https://docs.racket-lang.org/reference/Macros.html" rel="noreferrer">the full &quot;Macros&quot; reference</a></p>
</li>
<li><p><a href="https://docs.racket-lang.org/syntax/stxparse.html" rel="noreferrer">Reference</a> and <a href="https://www2.ccs.neu.edu/racket/pubs/icfp10-cf.pdf" rel="noreferrer">paper</a> of Racket's <code>syntax/parse</code></p>
</li>
</ul>

CC BY-SA 4.0
2023-11-19T16:52:44.093Z
2023-11-19T21:48:30.687Z
2023-11-19T21:48:30.687Z
tarzh
CC BY-SA 4.0
2023-11-19T17:58:06.903Z
Scheme requires additional parenthesis: `(a b) ...` instead of `$(a b)*`.
ceving
CC BY-SA 4.0
2023-11-19T18:09:57.213Z
The difference is that `(a b)` is one syntax node. In Scheme you can't match repetitions without the parenthesis (e.g. `a b a b a b`) without using `syntax-case` and manually checking, you can only match something like `(a b) (a b) (a b)`
tarzh
CC BY-SA 4.0
2023-11-19T21:49:09.913Z
Thanks, I added it as a new section
tarzh
# Is there a difference between Rust's `macro_rules!` and Scheme's `syntax-rules`?
Posted by: username_0 on 2023-11-19
This is an example for the use of Rust's macro_rules!:
macro_rules! add {
    ($a:expr,$b:expr) => {{
        $a + $b
    }};
    ($a:expr) => {{
        $a
    }};
}

A similar example for Scheme using syntax-rules could be:
(define-syntax add
  (syntax-rules ()
    ((_ a b) (+ a b))
    ((_ a)   a)))

One might think that this two language features are equivalent.  But is it the case or is there a hidden difference?
**Tags:** rust lisp meta-programming scheme
---
## 1 answers
### Answer by username_1 on 2023-11-19. Score: 16
[Rust's macros are directly inspired by Scheme's macros](https://www.reddit.com/r/rust/comments/azlqnj/prior_art_for_rusts_macros/ei8tf2a/), but they aren't the same.

First, I should clarify that while there is a formal specification to Scheme (R*n*RS), there are many more commonly used extended dialects, and each have their own `syntax-rules` implementations which are slightly different. Here I cite [R6RS](https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_chap_12), [Chez Scheme](https://www.scheme.com/csug8/syntax.html), [Guile](https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html) and [Racket](https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._syntax-rules%29%29). I believe all the similarities and differences listed apply between Rust and any of the Scheme dialects.

## Similarities

* All have **function-like syntax**, but unlike functions, are evaluated at compile-time.
* All do a sort of **pattern-match on the AST** provided and produce a new AST.
* All can have **multiple cases**, which are tried top-to-bottom.
* All have some form of **hygiene**, which prevents identifiers inside the macro from mixing with identifiers with the same name outside of the macro ([Rust](https://danielkeep.github.io/tlborm/book/mbe-min-hygiene.html), [R6RS](https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_sec_12.1), [Guile](https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html#Hygiene), [Racket](https://beautifulracket.com/explainer/hygiene.html)).
* All allow **recursive macros** (macros whose expansion calls themselves again) and **nested macros** (macros whose expansion defines other macros). For nested macros, there are ways to get [the `$` in Rust](https://stackoverflow.com/a/53971532) and [`...` in Scheme](https://stackoverflow.com/a/68743610) within the inner macros working correctly.
* All have a long list of "tricks" to create complicated macros (examples in [Rust](https://blog.cloudflare.com/writing-complex-macros-in-rust-reverse-polish-notation/), [2](https://cohost.org/poliorcetics/post/363003-a-rust-macro-trick-i), [Scheme](https://github.com/mnieper/scheme-macros), [2](https://okmij.org/ftp/Scheme/macros.html), [3](https://www.cs.rice.edu/%7Ejavaplt/311/Readings/macroslides.pdf); just Google "Rust macro tricks" and "Scheme macro tricks" for more examples).

## Differences

* **Rust macros may match syntax elements of particular types (e.g. `expr`)**. These syntax elements may actually consist of multiple syntax-nodes and require further parsing before they get matched (very complicated). `syntax-rules` only matches individual nodes (equivalent to `tt`), and matching is done after all parsing (very simple).
* **Rust has "optional fragments"** (`$(...)?`) which match zero-or-one repetition of a sequence of syntax-nodes, and `$(...)+` fragments which match one-or-many. Scheme only has `...` which matches zero-or-many repetitions of a syntax-node.
  + Related, **Rust's repetitions can contain multiple syntax-nodes** (which are matched in repeating sequence, e.g. `$(a b)*` matches `a b a b a b`), Scheme's repetitions only contain one.
  + Scheme patterns may also have a dotted-pair, which matches the remaining elements of a Scheme "list" including the dotted-pair if it ends in one. Rust has no analogue to dotted-pairs.
* Rust has a large set of [ambiguity rules](https://doc.rust-lang.org/reference/macro-ambiguity.html) due to its complicated macro parsing and forwards-compatibility with new syntax. **Scheme has no ambiguity rules, because there's no ambiguity to how the macro pattern is matched**, and Scheme's syntax will never change to introduce one.
* Macros in Rust can only be local to a module, unless you use [macros 2.0](https://github.com/rust-lang/rust/issues/39412) which is experimental and requires nightly Rust. **Scheme has `let-syntax` which creates its own local scope and defines macros which only apply to that scope** (e.g. `(let-syntax ([f (syntax-rules () [(_ x) x])]) (f 1))`).
* This is technically off-topic because it goes beyond `syntax-rules` and `macro_rules!`, but **the way that Rust and Scheme let you write more complicated macros are completely different**.
  + Rust's `macro_rules!` (AKA "Macro By Example") is very limited. If you want to "pull back the curtain" and manipulate syntax programmatically within the Rust language, you must use [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html). These must be defined in a separate `proc-macro` crate which the code which uses the macros must depend on. Moreover, proc-macros abandon Rust's powerful syntax pattern-matching and give you the raw stream of tokens to manipulate directly.
  + In contrast, **Scheme has [`syntax-case`](https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html) which is what `syntax-rules` expands to**. `syntax-case` still lets you pattern-match syntax, but instead of the RHS being the result AST, **`syntax-case` contains arbitrary Scheme code which gets evaluated and produces a ["syntax object"](https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html#node_sec_12.2)** like `#'(foo (bar . baz))`. **Macros defined with `define-syntax`/`let-syntax` and `syntax-case` can be used within the same file**, and Racket in particular even lets you define macros for other macros within the same file using statements like [`begin-for-syntax`](https://docs.racket-lang.org/reference/begin.html#%28form._%28%28quote._%7E23%7E25kernel%29._begin-for-syntax%29%29) (see [phase levels](https://docs.racket-lang.org/guide/phases.html))

## [`syntax/parse`](https://docs.racket-lang.org/syntax/stxparse.html): the real origin of Rust-style macros

Racket in particular has a newer macro system, `syntax/parse`, which is what specifically inspired Rust's macros. [`define-syntax-parse-rule`](https://docs.racket-lang.org/syntax/Defining_Simple_Macros.html) is the analogue to Rust's `macro_rules!`.

Like `macro_rules!`, `syntax/parse` can match different kinds of syntax using `:expr`, `:id`, etc. (called [syntax classes](https://docs.racket-lang.org/syntax/Library_Syntax_Classes_and_Literal_Sets.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._expr%29%29), although in Rust they're called [designators](https://doc.rust-lang.org/rust-by-example/macros/designators.html)). `syntax/parse` can also match repeating sequences of nodes using [the `~seq` syntax pattern](https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._%7E7eseq%29%29).

But `syntax/parse` is much more expressive than `macro_rules!`. You can [define your own syntax classes](https://docs.racket-lang.org/syntax/stxparse-specifying.html), and there are other [syntax patterns](https://docs.racket-lang.org/syntax/stxparse-patterns.html); e.g. [`~or`](https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28elem._%28pattern-link._%28%7E7eor%2A._s%29%29%29) lets you match local alternatives (e.g. `(number (~or 1 2 3))` matches `(number 1)` and `(number 3)`), and [`~not`](https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._%7E7enot%29%29) creates a macro which matches any syntax node *except* ones which match its argument. Moreover, `syntax/parse` provides [`syntax-parse`](https://docs.racket-lang.org/syntax/Parsing_Syntax.html), the analogue to `syntax-case`, which lets you evaluate arbitrary Racket code when producing the result syntax.

---

## Main references

* [Rust `macro_rules!` "Macros by Example"](https://doc.rust-lang.org/reference/macros-by-example.html)
* [R6RS `syntax-case`](https://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-Z-H-13.html) (which `syntax-rules` expands to)
* [Chez Scheme macros](https://www.scheme.com/csug8/syntax.html)
* [Guile Scheme `syntax-rules`](https://www.gnu.org/software/guile/manual/html_node/Syntax-Rules.html)
* [Racket scheme pattern-based macros](https://docs.racket-lang.org/guide/pattern-macros.html), [`syntax-rules`](https://docs.racket-lang.org/reference/stx-patterns.html#%28form._%28%28lib._racket%2Fprivate%2Fstxcase-scheme..rkt%29._syntax-rules%29%29) and [the full "Macros" reference](https://docs.racket-lang.org/reference/Macros.html)
* [Reference](https://docs.racket-lang.org/syntax/stxparse.html) and [paper](https://www2.ccs.neu.edu/racket/pubs/icfp10-cf.pdf) of Racket's `syntax/parse`
> **Comments:**
> **username_0** (2023-11-19): Scheme requires additional parenthesis: `(a b) ...` instead of `$(a b)*`.  
> **username_1** (2023-11-19): The difference is that `(a b)` is one syntax node. In Scheme you can't match repetitions without the parenthesis (e.g. `a b a b a b`) without using `syntax-case` and manually checking, you can only match something like `(a b) (a b) (a b)`  
> **username_1** (2023-11-19): Thanks, I added it as a new section  
---
