# Macro generating definitional macro

**URL:** <https://racket.discourse.group/t/macro-generating-definitional-macro/1004>\
**Category:** Questions & Answers\
**Tags:** question, macro\
**Created:** [May 19, 2022, 5:10am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004 "2022-05-19T05:10:28Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![rockn](https://avatars.discourse-cdn.com/v4/letter/r/7bcc69/32.png) [@rockn](https://racket.discourse.group/u/rockn)\
**Post date:** [May 19, 2022, 5:10am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004/1 "2022-05-19T05:10:28Z")

</div>

Hi,

```scheme
(define-syntax-rule (define-define-macro m id)
  (define-syntax-rule (m)
    (define id (lambda (x) x))))
(define-define-macro m1 id1)
(m1)
(id1 2)

```

Why is id1 unbound while

```scheme
(define-syntax-rule (m2 id)
    (define id (lambda (x) x)))
(m2 id2)
(id2 2)

```

id2 is well bound in this case?  
I understand how the expander gets this result but why is the first definition not allowed by the expander? How to understand this behaviour?

---

<div class="post-metadata">

**Author:** ![joskoot](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/joskoot/32/1964_2.png) [@joskoot](https://racket.discourse.group/u/joskoot)\
**Post date:** [May 19, 2022, 10:29am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004/2 "2022-05-19T10:29:12Z")

</div>

```scheme
The inner syntax rule hides id.

```

```scheme

```

```scheme
(define-syntax-rule (m)

```

`(define id (lambda (x) x))`

 ![DC369554041A4649A528B20B10730B39.png](https://global.discourse-cdn.com/free1/uploads/racket/original/1X/2ad4c25f471d2cfc2782215e9bb5a535b2545175.png)

---

<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:** [May 20, 2022, 2:51am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004/3 "2022-05-20T02:51:58Z")

</div>

When a `(define-syntax-rule pattern template)` macro is used, everything in `template` that does not appear in `pattern` has a _macro introduction scope_ attached to it. I'll annotate that in the expansion of the example code you gave by adding a superscript to each identifier with the introduction scope. I'll use `a` for the scope added by the `define-define-macro` macro, and `b` for the scope added by the inner `define-syntax-rule` macro:

```scheme
(define-define-macro m1 id1) ; next expansion step
(m1)
(id1 2)
=>
(define-syntax-ruleᵃ m1
  (defineᵃ id1 (lambdaᵃ (xᵃ) xᵃ)))
(m1) ; next expansion step
(id1 2)
=>
(define-syntax-ruleᵃ m1
  (defineᵃ id1 (lambdaᵃ (xᵃ) xᵃ)))
(defineᵃᵇ id1ᵇ (lambdaᵃᵇ (xᵃᵇ) xᵃᵇ))
(id1 2) ; next expansion step - error, id1 is unbound

```

When an identifier is defined with `(define id expression)`, the scopes on `id` matter. If there's a scope on `id`, that scope must be present on any other `id` for it to count as a usage of `id`. So here's where we hit the problem: in this expansion, the `id1` in the `(id1 2)` expression does not have the `b` scope that's present in the `(defineᵃᵇ id1ᵇ (lambdaᵃᵇ (xᵃᵇ) xᵃᵇ))` definition. So that definition can't be used, leaving the `id1` unbound.

More succinctly, if a macro introduces a definition - that is, the template of a macro defines an identifier and that identifier did not come from the macro's input pattern - that definition is invisible at the macro's use site.

You can undo this with `syntax-local-introduce` if you wish, but that's an advanced tool that's not suitable for most problems. I recommend reading and understanding [Bindings as Sets of Scopes](https://www.cs.utah.edu/plt/scope-sets/) to learn more.

---

<div class="post-metadata">

**Author:** ![rockn](https://avatars.discourse-cdn.com/v4/letter/r/7bcc69/32.png) [@rockn](https://racket.discourse.group/u/rockn)\
**Post date:** [May 20, 2022, 9:22am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004/4 "2022-05-20T09:22:10Z")

</div>

Thanks for the detailed explanation.

I actually came up with this question while reading [Bindings as Sets of Scopes](https://www.cs.utah.edu/plt/scope-sets/).

But the expander should be able to detect that id1 actually comes from the top-level definition context. Is there a rationale why the inner definition macro should not affect the top-level definition context?

---

<div class="post-metadata">

**Author:** ![ryanc](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ryanc/32/71_2.png) [@ryanc](https://racket.discourse.group/u/ryanc)\
**Post date:** [May 20, 2022, 10:24am UTC](https://racket.discourse.group/t/macro-generating-definitional-macro/1004/5 "2022-05-20T10:24:45Z")

</div>

_Everything_ ultimately comes from the top-level context.

Your argument seems to be that `id` should not be treated as introduced by `m` because it was an argument to `define-define-macro`. But consider this example:

```scheme
(define-syntax-rule (m0)
  (define id (lambda (x) x))

```

Then your argument would imply that `id` should not be treated as introduced by `m0`, because it was an argument to `define-syntax-rule`, which is actually a macro that expands into `define-syntax` and `syntax-case**` and `syntax/loc`. But I hope we agree that hygiene requires that `id` be treated as introduced and thus not accessible at the use site.

If you think those two cases should be treated differently, what rule distinguishes them? And what is the cost in complexity?

Lest I give the wrong impression, your question is a great question, and I remember my frustration when I first discovered that hygienic macros did not define abstractions that were "pure" or "baggage-free" in the way that `lambda` abstractions are. Other examples of abstractions with "baggage" include function abstraction in languages like C or JavaScript: in both languages, the target of `return` changes; in JavaScript, you can reflectively access the current function's name and arguments; in C, it changes the lifetime of stack-allocated variables. And so on.
