# Macro that expands to (module .. (provide (all-defined-out)) ..)

**URL:** <https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270>\
**Category:** Questions & Answers\
**Tags:** macro\
**Created:** [May 28, 2026, 9:21pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270 "2026-05-28T21:21:14Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [May 28, 2026, 9:21pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/1 "2026-05-28T21:21:14Z")

</div>

I'm attempting to create a macro that expands to a submodule, but I can't seem to figure out how to use the (provide (all-defined-out)) form indroduced by the macro. Here is an example:

```scheme
#lang racket/base

(module defpkg racket/base
  (require (for-syntax racket/base
                     syntax/parse
                     racket/syntax))
  (provide (all-defined-out))

  (define-syntax (define-package stx)
    (syntax-parse stx
      [(_ name:id e:expr ...+)
       #:with register! (format-id #'name "~a" 'register!)
       #'(module* name #f
           (provide (all-defined-out)) ;; <- doesn't work!
           (define (register!)
             (displayln "registered!"))
           ;; for racket bindings
           e ...)])))

(module pkgs racket/base
  (require (submod ".." defpkg))
  (provide (all-defined-out))

  ;; a is provided from module 'pkgs
  (define a 1)
  
  ;; sub module* directly
  (module* pkg0 #f
    (provide (all-defined-out))
    ;; b is provided from (submod 'pkgs pkg0)
    (define b (+ a 1)))
  
  ;; sub module* through macro
  (define-package pkg1
    ;(provide (all-defined-out)) ;; <- will work if uncommented
    ;; c is provided from (submod 'pkgs pkg1)
    (define c (+ a 2)))
  )

(let ()
  ;; a from 'pkgs
  (local-require 'pkgs)
  (displayln a)
  ;; b from (submod 'pkgs pkg0)
  (local-require (submod 'pkgs pkg0))
  (displayln b)
  ;; c and register! from (submod 'pkgs pkg1)
  (local-require (submod 'pkgs pkg1))
  (displayln c)
  (register!))

```

I'm assuming that the provide form introduced in (define-package ..) is in the wrong scope, but I have been unable to figure it out how to fix it. Does anyone have suggestions?

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [May 29, 2026, 1:09am UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/2 "2026-05-29T01:09:37Z")

</div>

> [@wluker](#):
>
> I'm assuming that the provide form introduced in (define-package ..) is in the wrong scope, but I have been unable to figure it out how to fix it. Does anyone have suggestions?

The scope is “on the parentheses” of `(all-defined-out)`, a common pattern for this sort of form. Concretely:

```scheme
(define-syntax (define-package stx)
  (syntax-parse stx
    [(_ name:id e:expr ...+)
     #:with register! (format-id #'name "~a" 'register!)
     #:with ADO (datum->syntax #'name (list #'all-defined-out))
     #'(module* name #f
         (provide ADO)
         (define (register!)
           (displayln "registered!"))
         ;; for racket bindings
         e ...)]))

```

To explain a bit more, as you guessed `all-defined-out` doesn’t _literally_ provide “ **all** defined,” because macros should be able to generate module-level definitions for their internal purposes with the protection of hygiene. What it actually provides, as [the docs explain in more detail](https://docs.racket-lang.org/reference/require.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._all-defined-out%29%29), is everything that defined **and** “accessible from the lexical context of the `(all-defined-out)` form.”

But what _is_ “the lexical context of the `(all-defined-out)` form”? It _can’t_ be the context of the identifier `all-defined-out`, because a macro like your `define-package` that generates a use of `(all-defined-out)` should be able to work even if `all-defined-out` isn’t in scope where `define-package` is used. Some macros have a sub-form like `name` that might make sense to use for the relevant scope, but sometimes there either isn’t a subform (as with `all-defined-out`) or the desired scoping behavior has more dimensions to keep distinct. So, often, the scope “on the parentheses” of the form as a whole.

* * *

Incidentally, I used `#'name` because you were already giving that scope to `register!`, but you might consider making `register!` a [syntax parameter](https://docs.racket-lang.org/reference/stxparam.html), instead, in which case you might want to use the scope “on the parentheses” of `define-package`, i.e. `stx` or [`this-syntax`](https://docs.racket-lang.org/syntax/stxparse-specifying.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._this-syntax%29%29). I’m guessing you don’t mean for every package to export `register!`.

---

<div class="post-metadata">

**Author:** ![ndykman](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ndykman/32/2140_2.png) [@ndykman](https://racket.discourse.group/u/ndykman)\
**Post date:** [May 29, 2026, 2:05am UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/3 "2026-05-29T02:05:13Z")

</div>

> [@wluker](#):
>
> I'm assuming that the provide form introduced in (define-package ..) is in the wrong scope, but I have been unable to figure it out how to fix it. Does anyone have suggestions?

Just to following on to @LiberalArtist excellent response, one way to figure this out is to use the macro stepper and select the `[menu]->Stepper->View Syntax Properties`. This confirmed that, indeed, the `all-provided-out` part of the syntax was scoped by the `defpkg` module and was not in the scope that `module*` would create. I was stuck on how to fix that.

What the solution does is to force the `(all-provided-out)` list to be scoped by the expanded syntax; which means that it scoped to the module created by `module*`. Honestly, I probably never would have gotten there. so I am glad somebody had an answer.

This is an example of how tricky it can be in syntax patterns to see what is actually defined in the scope of the created syntax versus the environment in which the syntax transformer is being executed in.

Nathan

---

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [May 29, 2026, 1:43pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/4 "2026-05-29T13:43:25Z")

</div>

Even with these wonderful answers in front of me I had to reread them a couple of times before it clicked. Thank you both!

---

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [May 29, 2026, 8:08pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/5 "2026-05-29T20:08:39Z")

</div>

Since no good deed goes unpunished... I have a follow up question on how to go the opposite direction. The example below now uses (module ..) instead of module\*, so now the issue is that the (define ..) sent into the define-package macro doesn't have the correct scope set for the (module ..). define is not defined.

```scheme
#lang racket/base

(module defpkg racket/base
  (require (for-syntax racket/base
                       syntax/parse
                       racket/syntax))
  (provide (all-defined-out))
  
  (define-syntax (define-package stx)
    (syntax-parse stx
      [(_ name:id e:expr ...)
       #:with (e+ ...) (for/list ([e- (syntax-e #'(e ...))])
                         e- ;; <- how to rescope it to be within (module ..)
                         #;(datum->syntax #f (syntax->datum e-)))
       #'(module name racket/base
           (provide (all-defined-out))
           e+ ...)])))

(require 'defpkg)
(define-package pkg2
  (define b 2))

(let ()
  (local-require 'pkg2)
  (displayln b))

```

(datum-\>syntax #f (syntax-\>datum #'e)) was my best guess, but obviously wrong. Any ideas on how I should rescope e ... so that it points to the racket/base within (module name racket/base e ...)? I played around with some of the namespace utilities, but failed there as well.

---

<div class="post-metadata">

**Author:** ![EmEf](https://avatars.discourse-cdn.com/v4/letter/e/53a042/32.png) [@EmEf](https://racket.discourse.group/u/EmEf)\
**Post date:** [May 29, 2026, 8:23pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/6 "2026-05-29T20:23:30Z")

</div>

Do you mean

```scheme
#lang racket/base

(module defpkg racket/base
  (require (for-syntax racket/base
                       syntax/parse
                       racket/syntax))
  (provide (all-defined-out))
  
  (define-syntax (define-package stx)
    (syntax-parse stx
      [(_ name:id e:expr ...)
       #:with (e+ ...) (for/list ([e- (syntax-e #'(e ...))])
                         e- ;; <- how to rescope it to be within (module ..)
                         (datum->syntax #'this-syntax (syntax->datum e-)))
       #'(module name racket/base
           (provide (all-defined-out))
           e+ ...)])))

(require 'defpkg)
(define-package pkg2
  (define b 2))

(let ()
  (local-require 'pkg2)
  (displayln b))

```

---

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [May 29, 2026, 9:04pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/7 "2026-05-29T21:04:53Z")

</div>

Yes! Very nice.

It's good to know I was close:)

From playing with it, it appears that it doesn't matter what the syntax object is as long as it is new. So #'this-syntax could be #'anything I guess.

Trying to make sense of this... datum-\>syntax is a macro so #'this-syntax will not get evaluated until (module ..) is being expanded, so the scope will be within the new module. Does this sound right?

Thank you.

---

<div class="post-metadata">

**Author:** ![EmEf](https://avatars.discourse-cdn.com/v4/letter/e/53a042/32.png) [@EmEf](https://racket.discourse.group/u/EmEf)\
**Post date:** [May 29, 2026, 9:24pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/8 "2026-05-29T21:24:27Z")

</div>

I could have used `#’stx` too. `datum->syntax` is a function, and it transfers the lexical context of the given syntax to the newly created syntax object. To get DrRacket’s arrows and such you also want to supply optional arguments.

See [https://docs.racket-lang.org/reference/stxops.html#%28def._%28%28quote._~23~25kernel%29.\_datum-~3esyntax%29%29](https://docs.racket-lang.org/reference/stxops.html#%28def._%28%28quote._~23~25kernel%29._datum-~3esyntax%29%29)

— Matthias

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [May 30, 2026, 3:30am UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/9 "2026-05-30T03:30:35Z")

</div>

> [@ndykman](#):
>
> This confirmed that, indeed, the `all-provided-out` part of the syntax was scoped by the `defpkg` module and was not in the scope that `module*` would create. I was stuck on how to fix that.
> 
> What the solution does is to force the `(all-provided-out)` list to be scoped by the expanded syntax; which means that it scoped to the module created by `module*`. Honestly, I probably never would have gotten there. so I am glad somebody had an answer.

I didn’t figure it out, either, until [Matthew explained it to me](https://groups.google.com/g/racket-users/c/IHMG5AtH_Zs/m/I4qiKot2AAAJ).

Now that I know the answer, I can see that the docs do specify this behavior—but the fact that it’s a recurring question suggests that an example or further explanation might be in order.

(The scope “on the parentheses” pattern comes up in other contexts, too, [e.g. with `submod`](https://racket.discourse.group/t/providing-name-only-to-sub-module/3918/11), but I’m not sure where to potentially put documentation on the pattern in general.)

* * *

> [@wluker](#):
>
> From playing with it, it appears that it doesn't matter what the syntax object is as long as it is new. So `#'this-syntax` could be `#'anything` I guess.

If you were referring to my suggestion above:

> [@LiberalArtist](#):
>
> you might want to use the scope “on the parentheses” of `define-package`, i.e. `stx` or [`this-syntax`](https://docs.racket-lang.org/syntax/stxparse-specifying.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._this-syntax%29%29).

the `this-syntax` I was referring to is an expression form. You wouldn’t generally quote it with `#'`, or it would just be equivalent to `#'cons` or something, an identifier with the lexical context from the use of the `#'` form. Used as an expression, `this-syntax` refers to the syntax object currently being parsed.

Here’s an illustration of the difference:

```scheme
#lang racket
(module demo racket
  (provide demo)
  (require syntax/parse/define)
  (define-syntax-parser demo
    [_
     #`(values
        (list "quoted" (quote-syntax #,#'this-syntax))
        (list "as expression" (quote-syntax #,this-syntax)))]))
(require 'demo)
(demo arbitrary sub-expressions here)
#| ; in DrRacket, the syntax objects will be printed as snips you can
   ; interactively inspect, not just plaintext
'("quoted" #<syntax:demo.rkt:8:41 this-syntax>)
'("as expression" <syntax:demo.rkt:11:0 (demo arbitrary sub-expressio...>)
|#

```

* * *

> [@wluker](#):
>
> I have a follow up question on how to go the opposite direction. The example below now uses (module ..) instead of module\*, so now the issue is that the (define ..) sent into the define-package macro doesn't have the correct scope set for the (module ..). define is not defined.

Matthias’ solution directly answers the question you posed, `e- ;; <- how to rescope it to be within (module ..)`, but I would suggest a slightly different approach:

```scheme
#lang racket/base

(module defpkg racket/base
  (require (for-syntax racket/base
                       syntax/parse
                       racket/syntax))
  (provide (all-defined-out))
  
  (define-syntax (define-package stx)
    (syntax-parse stx
      [(_ name:id e:expr ...)
       #:with (R/B e+ ...) ((make-syntax-introducer)
                            #`(#,(datum->syntax stx 'racket/base)
                               e ...))
       #`(module name R/B
           (provide #,(datum->syntax #'R/B (list #'all-defined-out)))
           e+ ...)])))

(require 'defpkg)
(define-package pkg2
  (define all-defined-out
    "doing this breaks the other approach")
  (define b 2))

(let ()
  (local-require 'pkg2)
  (displayln b))

```

The core idea in this variant is using `(datum->syntax stx 'racket/base)` to bring the bindings from the synthesized module’s initial import into scope for the input sub-forms, rather than changing the input sub-forms to use the scope of the `define-package` implementation. (Note that Matthias’ version could have used `#'cons` just as well as `#'this-syntax`, and it would not work with un-`#'`ed `this-syntax` or `stx`.) It requires knowing the fairly obscure fact (I can’t find a reference in the docs to cite for it ATM) that `module` uses the scope on the term in the language position this way.

Using `syntax->datum` on complex sub-forms is generally a warning sign that you should look for a better solution. It is very difficult to use correctly and robustly, because `syntax->datum` throws away all of the scope, source-location information, and properties. (Matthias’ comment about the other arguments to `datum->syntax` alludes to some of this.) For example, consider a macro that expands to `define-package` with a body that contains both user code and macro-introduced terms: using `syntax->datum` on the body will throw out the macro-introduction scope, breaking hygiene for the downstream macro.

The other change, which was a workaround for a problem that surprised me along the way, is the use of `(make-syntax-introducer)` to add an additional fresh scope to `racket/base` and the body forms (and, indirectly, “on the parentheses” of `(all-defined-out)`) that is **not** on the identifiers `provide` and `all-defined-out`. I had anticipated the need to deal with potential shadowing of `provide` and `all-defined-out`: what surprised me was that, without `(make-syntax-introducer)` (e.g. replacing it with `values`), I got an ambiguous binding error for `provide`. I haven’t yet worked out why.

> **\[the full ambigous-binding error message\]**
>
> ``` provide: identifier's binding is ambiguous context...: matching binding...: #(provide # 0) #(64982 module) #(129967/2 module defpkg) matching binding...: #(provide # 0) #(65179 module) #(130361/2 module pkg2) in: provide```

I’d also initially tried to use `syntax-local-introduce` instead of `datum->syntax`, and I haven’t yet thought through why that didn’t work as I’d hoped.

I think it’s worth noting that macro-introduced submodules are a fairly advanced corner of macrology, and there are some rough edges. It’s been a few years since I did much with it, and not all of the pitfalls are fresh in my mind.

---

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [May 30, 2026, 5:34pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/10 "2026-05-30T17:34:54Z")

</div>

> I think it’s worth noting that macro-introduced submodules are a fairly advanced corner of macrology

For me, my belief that I could just slap a macro together to introduce submodules is a testament to how easy Racket makes so many other things.

---

<div class="post-metadata">

**Author:** ![wluker](https://avatars.discourse-cdn.com/v4/letter/w/0ea827/32.png) [@wluker](https://racket.discourse.group/u/wluker)\
**Post date:** [June 1, 2026, 2:30pm UTC](https://racket.discourse.group/t/macro-that-expands-to-module-provide-all-defined-out/4270/11 "2026-06-01T14:30:21Z")

</div>

To finish this out I added (with-packages ...) that seems to work for hopefully the right reasons.

```scheme
#lang racket/base

(module defpkg racket/base
  (require racket/stxparam)
  (require (for-syntax racket/base
                       syntax/parse
                       racket/syntax))
  (provide (all-defined-out))
  
  (define-syntax (define-package stx)
    (syntax-parse stx
      [(_ name:id e:expr ...)
       #:with (R/B e+ ...) ((make-syntax-introducer)
                            #`(#,(datum->syntax stx 'racket/base)
                               e ...))
       #`(module name R/B
           (provide #,(datum->syntax #'R/B (list #'all-defined-out)))
           e+ ...)]))

  (define-syntax-parameter current-with-package-scope #f)

  (define-syntax (with-packages stx)
    (syntax-parse stx
      [(_ ([path:expr ... pkg:expr] ...) e ...+)
       #:with scope
       (if (syntax-parameter-value #'current-with-package-scope)
           (syntax-local-introduce (syntax-parameter-value #'current-with-package-scope))
           stx)
       #:with (sm-path ...) (syntax-e #'([path ... pkg] ...))
       #:with (SUBMOD ...)
       (for/list ([pth (syntax-e #'(sm-path ...))]
                  ;; location for the drracket binding arrows
                  [loc (syntax-e #'(pkg ...))])
         (if (equal? (length (syntax-e pth)) 1)
             (datum->syntax #'scope `(,#'submod "." ,@(syntax-e pth)) loc loc)
             (datum->syntax #'scope `(,#'submod ,@(syntax-e pth)) loc loc)))
       #'(let ()
           ;; import packages into this local module scope
           (local-require SUBMOD) ...
           e ...)]))

  (define-syntax (with-package-scope stx)
    (syntax-parse stx
      [(_ scope e ...+)
       ;; syntax parameter to track where the bound ids in e... may originate from
       ;; this is required by with-packages
       #'(syntax-parameterize ([current-with-package-scope
                                (if (syntax-parameter-value #'current-with-package-scope)
                                    (syntax-parameter-value #'current-with-package-scope)
                                    (quote-syntax scope))])
           e ...)])))

(require 'defpkg)
(define-package pkg2
  (define all-defined-out
    "doing this breaks the other approach")
  (define b 2))

(let ()
  (local-require 'pkg2)
  (displayln b))

(with-packages ([pkg2])
  (displayln b))

(module test racket/base
  (require (for-syntax racket/base
                       syntax/parse
                       racket/syntax))
  (require (submod ".." defpkg))

  (with-packages ([".." pkg2])
    (displayln b))
    
  
  (define-syntax (another-macro stx)
    (syntax-parse stx
      [(_ e ...)
       #`(let ()
           (with-package-scope #,stx
             (with-packages ([".." pkg2])
               e ...)))]))

  (another-macro
   (displayln b)))

```

Thank you all for the help. I have learned quite a bit here.
