# Macros expanding to definitions

**URL:** <https://racket.discourse.group/t/macros-expanding-to-definitions/2301>\
**Category:** General\
**Tags:** macro, define, scope\
**Created:** [September 10, 2023, 3:23pm UTC](https://racket.discourse.group/t/macros-expanding-to-definitions/2301 "2023-09-10T15:23:30Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [September 10, 2023, 3:23pm UTC](https://racket.discourse.group/t/macros-expanding-to-definitions/2301/1 "2023-09-10T15:23:30Z")

</div>

# Macros expanding to definitions

This post is about macros that expand into definitions.

The examples will use `syntax/parse`, so all snippets  
work if they are preceded with:

```scheme
#lang racket
(require (for-syntax racket/syntax syntax/parse))

```

## Scope sets and bindings

The first example is the macro `define-a`.

```scheme
1 (define-syntax (define-a stx)
2 (syntax-parse stx
3 [(_define-a expr)
4 #'(define a expr)]))
5
6 (define-a 42)
7 a ; unbound identifier

```

The variable reference `a` on line 7 results in an unbound identifier error.  
Why?

During macro expansion the expander attaches sets of scopes to each identifier.

When the macro application `(define-a 42)` is expanded a new scope `s`,  
is created and attached to the variables bound in the syntax fragment  
produced by the macro. The expansion becomes:

```scheme
(define a_{s} 42)

```

The variable reference `a` on line 7, has no extra scopes attached,  
so the reference is a\_{}.

The scope sets are used to determine whether one variable can bind another.

```
A reference’s binding is found as one whose set of scopes is the
largest subset of the reference’s own scopes (in addition to having
the same symbolic name).

```

We now see, why a\_{s} doesn't bind a\_{}. The problem is that {s}  
is not a subset of {}.

## Copying the lexical context

If we want the definition to bind a\_{} we must produce an identifier a,  
that has no extra scopes. We can use `format-id` (or `datum->syntax`)  
for this. A little simplified, a call to `format-id` looks like this:

```
(format-id lexical-context format-string fmt value ...)

```

The syntax transformer for `define-a` receives the syntax object  
to expand `(define-a 42)` as the argument `stx`. Using `stx` as  
the lexical context for `a` copies the scope set attached to `stx`  
to our new identifier. Effectively giving us a an identifer `a`  
with the same lexical context as `(define-a 42)`. And that  
context is the same as the reference `a` on line 9.

```scheme
1 (define-syntax (define-a stx)
2 (syntax-parse stx
3 [(_define-a expr)
4 (define a (format-id stx "~a" 'a))
5 (with-syntax ([a a])
6 #'(define a expr))]))
7  
8 (define-a 42)
9 a ;; evaluates to 42

```

Now our `define-a` introduces an `a` defined in the context where the macro was called.

## Expansion to a macro, that expands into a definition

Let's attempt to define a macro that uses `define-a` in the expansion.

```scheme
 1 (define-syntax (define-a stx)
 2 (syntax-parse stx
 3 [(_define-a expr)
 4 (define a (format-id stx "~a" 'a))
 5 (with-syntax ([a a])
 6 #'(define a expr))]))
 7 
 8 (define-syntax (define-a42 stx)
 9 (syntax-parse stx
10 [(_define-a42)
11 #'(define-a 42)]))
12       
13 (define-a42)
14 a ;; unbound variable

```

The intention was that `define-a42` where to define a variable a with  
an initial value 42. However, we get an unbound identifier error again.

Can you see why?

Let's attempt to track the expansion of `(define-a42)`.  
The transformer returns #'(define a 42) but each macro call  
introduces a new scope, so if we call the new scope t, it returns:

```
(define-a_{t} 42)

```

The expander then calls the transformer of `define-a` with stx  
bound to `(define-a_{t} 42)`. That macro call introduces a new scope s.  
The return value will be [we are ignoring the scope set for define]:

```
(define a_{t} 42)

```

Since the scope set {t} is not a subset of {} a\_{t} does not bind a\_{}.

## Fixing the example

Since `define-a` uses the context of `stx` for the variable `a`,  
we can fix the example above like this:

```scheme
(define-syntax (define-a42 stx)
  (syntax-parse stx
    [(_define-a42)
     (syntax-local-introduce
      #'(define-a 42))]))

```

The `syntax-local-introduce` removes the scope normally  
added by each macro call.
