# Confused about setup for reader and expander for simple DSL

**URL:** <https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859>\
**Category:** Questions & Answers\
**Created:** [July 17, 2025, 5:30pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859 "2025-07-17T17:30:47Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 17, 2025, 5:30pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/1 "2025-07-17T17:30:47Z")

</div>

I'm working on the [Beautiful Racket "wires" DSL](https://beautifulracket.com/wires/intro.html) without looking at his solution, or using the various `br` libraries -- I want to get things working using standard Racket and not rely on BR's syntactic sugar / setup.

I have a macro that correctly expands the DSL code into working Racket code. But I'm struggling with the boilerplate of making the `wires` code work with `#lang wires` and getting the reader and expander working.

I'm trying to get the simplest, most straightforward way of taking some `wires` code with `#lang wires` at the top and executing it.

I installed `wires` as a package using `raco`, but in `main.rkt`, I'm struggling with `read-syntax` and various `module-begin` stuff.

I have this right now:

```scheme
#lang racket

(require racket/port)

(define (read-syntax path port)
  (define src-lines (port->lines port))
  (define src-datums
    (for/list ([line src-lines])
      (read (open-input-string (format "(wires-operator ~a)" line)))))
  (define module-datum `(module wires "main.rkt"
                          ,@src-datums))
  (datum->syntax #f module-datum))

(provide read-syntax)

(provide (rename-out [wires-expander %#module-begin]))
(define-syntax-rule (wires-expander EXPR ...)
  #'(#%module-begin
     (displayln "wires expander ")
     EXPR ...))

```

And in the REPL, if I do `(read-syntax "wires-test.rkt" (open-input-file "wires-test.rkt") )`, I get something that looks good:

```scheme
#<syntax (module wires "main.rkt"
           (wires-operator)
           (wires-operator)
           (wires-operator // I am also allowing comments!)
           (wires-operator)
           (wires-operator x AND y -> d)
           (wires-operator x OR y -> e)
           (wires-operator x LSHIFT 2 -> f)
           (wires-operator y RSHIFT 2 -> g)
           (wires-operator NOT x -> h)
           (wires-operator NOT y -> i)
           (wires-operator 123 -> x)
           (wires-operator 456 -> y))>

```

(Here `wires-operator` is my macro, and it works the want I want for those inputs.)

But if I try to execute that code from the command line, I get:

```scheme
$ racket wires-test.rkt
module: no #%module-begin binding in the module's language
in: (module wires "main.rkt" (wires-operator ...

```

I'm confused about all these modules, module-begins, and so on.

1. What exactly does `read-syntax` need to put in its output? What kind of module should it output?
2. How do I provide the expander for that, so Racket can take the `read-syntax` output, run it to convert it into valid Racket code, and execute that? I thought I was doing that with my `provide`.

---

<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 17, 2025, 7:06pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/2 "2025-07-17T19:06:23Z")

</div>

TL;DR: you probably don’t want a relative module path in the initial import section of your generated module form. Instead, try `(module <any-name-here> wires ,@src-datums)`.

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 17, 2025, 8:07pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/3 "2025-07-17T20:07:11Z")

</div>

> [@benknoble](#):
>
> `(module <any-name-here> wires ,@src-datums)`.

I blindly tried that, and I get the same behavior in the REPL (it works and produces the expected syntax object) but now on the command line I get:

```scheme
default-load-handler: expected a `module' declaration, but found something else
  file: ..../wires-test.rkt

```

that's mysterious -- the returned syntax object, AFAICS, is nothing _but_ a module declaration! It's effectively just what I have above.

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 17, 2025, 8:12pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/4 "2025-07-17T20:12:03Z")

</div>

I do see one tiny typo - in my "require provide out", I transposed the # and % for module-begin. But fixing that doesn't change anything, so it does seem like Racket isn't connecting the output from read-syntax to the module-begin thing, which, to my understanding, is what will actually run the macro that, well, expands the code into actual Racket code.

---

<div class="post-metadata">

**Author:** ![shhyou](https://avatars.discourse-cdn.com/v4/letter/s/ccd318/32.png) [@shhyou](https://racket.discourse.group/u/shhyou)\
**Post date:** [July 17, 2025, 11:14pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/5 "2025-07-17T23:14:57Z")

</div>

`wires/main.rkt` needs to include a `reader` submodule that connects `read-syntax` to the `#lang`, like this code:

> **[17.3.4 Installing a Language](https://docs.racket-lang.org/guide/language-collection.html)**

In BR "THE READER" section, the `(module reader ...` is not specific to `#lang br/quicklang`, but also for any `#lang` setup.

The `syntax/module-reader` language in 17.3.3 in Racket Guide is helpful for writing such `reader` submodules. That is, rather than `(module reader racket (provide (rename-out [my-read-syntax read-syntax])) ...)`, it's likely more convenient to write `(module reader syntax/module-reader ...)`

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 18, 2025, 12:49pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/6 "2025-07-18T12:49:00Z")

</div>

> [@shhyou](#):
>
> `wires/main.rkt` needs to include a `reader` submodule that connects `read-syntax` to the `#lang`, like this code:

I copied the `literal` stuff from there, and that example works. (So does the "`dollar`" stuff in that part of the documentation.)

When I have

```scheme
(module reader syntax/module-reader

```

which you say is more convenient, I get an unbound identifier for `port->lines`, even if I `require racket/port` inside the module.

What's more convenient about `syntax/module-reader`?

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 18, 2025, 1:00pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/7 "2025-07-18T13:00:35Z")

</div>

I've made some progress, but now I'm running into this strange error: my reader seems to work, but with this code for the expander:

```scheme
(provide (rename-out [wires-expander #%module-begin]))
(define-syntax-rule (wires-expander EXPR ...)
  #'(#%module-begin
     (displayln "wires expander ")
     EXPR ...))

```

when I run my test file, I get:

```scheme
main.rkt:28:2: module: expansion of #%module-begin is not a #%plain-module-begin form
  at: (t-quote-syntax (#%module-begin (displayln "wires expander ") (wires-operator) ... (wires-operator x LSHIFT 2 -> f)...

```

The documentation for `plain-module-begin` isn't helpful. What do I need to do here to get that to expand correctly?

---

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [July 18, 2025, 1:08pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/8 "2025-07-18T13:08:12Z")

</div>

Not @shhyou, but from my limited personal experience, when I want to mess around with the reader, it is very convenient to have a setup like the following:

```scheme
main.rkt ;; for testing the system
syntax.rkt ;; whatever transformers I need to use during expansion
reader.rkt ;; the linking up I think you're busy with here

```

In particular, `syntax/module-reader` can be used like so:

```scheme
;; reader.rkt
#lang s-exp syntax/module-reader
racket/base
#:read my-read
#:read-syntax my-read-syntax
 
... rest of your definitions ...

```

which can then be used like so:

```scheme
;; main.rkt
#lang reader "reader.rkt"
(require
  "syntax.rkt") ;; for argument's sake

```

This all comes from the same set of example material you are looking at, I think, if you are referencing the `dollar` guides.

[17.3.3 Using #lang s-exp syntax/module-reader](https://docs.racket-lang.org/guide/syntax_module-reader.html)

I hope that helps a bit, I found it quite useful once I got the basic parts working, although I must admit I am hazy on the precise details.

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 18, 2025, 9:01pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/9 "2025-07-18T21:01:44Z")

</div>

> [@bakgatviooldoos](#):
>
> t is very convenient to have a setup like the following:

I copied your setup -- and it works, mostly! Now I get an error about `port->lines` being an unbound identifier. Any ideas about that?

With a different setup, it works...mostly.

Now my problem is this mysterious one: when I run my test file, I get:

```scheme
wires-operator: bad syntax          
  in: (wires-operator x AND y -> d)   
  context...:             
   /usr/share/racket/collects/syntax/wrap-modbeg.rkt:46:4   

```

This is weird, because handling that syntax is exactly what the `wires-operator` macro does! In the REPL, it works perfectly:

```scheme
syntax.rkt> (wires-operator 123 -> x)
syntax.rkt> (wires-operator 456 -> y)
syntax.rkt> (wires-operator x AND y -> d)
syntax.rkt> (d)
72

```

My `syntax.rkt` starts with:

```scheme
#lang racket

(define-syntax wires-operator
                 ;; literals:
  (syntax-rules (AND OR LSHIFT RSHIFT NOT -> //)
    [(wires-operator lhs AND rhs -> dest)
     (define (dest) (wires-and (lhs) (rhs)))]
  ....other cases and support functions...

```

How does that work in the REPL but not when run as a script? Why is it not expanding the macro there? It seems like the reader is doing what I expect?

---

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [July 18, 2025, 10:22pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/10 "2025-07-18T22:22:54Z")

</div>

> [@ddrake](#):
>
> Now I get an error about `port->lines` being an unbound identifier. Any ideas about that?

I couldn't say after messing about a bit.

However, I feel I may have led you astray somewhat, now that I am looking at the way you are approaching the story.

The `syntax/module-reader` seems to abstract more heavily over certain things, and the fact that I specifically use an example with the [`s-exp`](https://docs.racket-lang.org/reference/reader.html#%2528mod-path._s-exp%2529) "meta-lang" was probably not helpful. Long-story short, I am guessing that defining a module-wide transformer as you are trying is not going to work that way (but I speak under correction).

I got the following to work, more or less, after almost killing my poor computer due to something causing an endless stream of newlines.

This is taking again from the docs under the [`#lang reader` section](https://docs.racket-lang.org/guide/hash-lang_reader.html).

```scheme
;; wires-reader.rkt

#lang racket/base

(provide
  (rename-out
   [my-read read]
   [my-read-syntax read-syntax]))

(require
  racket/port
  racket/string)

(define (my-read in)
  (syntax->datum
   (my-read-syntax #false in)))

(define (operator line)
  (open-input-string (format "(wires-operator ~a)" line)))
 
(define (my-read-syntax src port)
  (define src-datums
    (for/list ([line (in-list (port->lines port))]
               #:when (non-empty-string? line))
      (read (operator line))))
  (define module-datum
    `(module wires racket/base
       (require "wires-syntax.rkt")
       ,@src-datums))
  (datum->syntax
   #false module-datum))

```

```scheme
;; wires-syntax.rkt

#lang racket/base

(provide
  wires-operator)

(define-syntax-rule (wires-operator ex ...)
  '(ex ...))

```

```scheme
;; main.rkt

#lang reader "wires-reader.rkt"

x AND y -> d

```

```scheme
'(x AND y -> d)

```

I am learning new things, too!

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 19, 2025, 12:00pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/11 "2025-07-19T12:00:51Z")

</div>

> [@bakgatviooldoos](#):
>
> I got the following to work, more or less, after almost killing my poor computer due to something causing an endless stream of newlines.

Yikes, don't kill your computer!

Thank you for the code -- it works almost exactly how I'd like it to. I've posted my current code: [GitHub - dandrake/wires-racket-dsl: a Racket-based domain-specific language for solving the "wires" programming puzzle](https://github.com/dandrake/wires-racket-dsl)

From here, my goals are:

- clean up the code and make it as simple and obvious as possible, so that I can use this as a template for other simple DSLs;
- get it working so that I can do `#lang wires` instead of specifying the reader. This is mostly aesthetic, but it would be nice to figure out how to get this to work.

---

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [July 19, 2025, 12:33pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/12 "2025-07-19T12:33:34Z")

</div>

That sounds like a great idea! I agree about making the `#lang` "just work".

As some interesting reading in this vein--making DSLs simple--see [#langs that Fit In Your Head – Terminally Undead](https://countvajhula.com/2024/12/09/langs-that-fit-in-your-head/), by @countvajhula.

For what it's worth, regarding the "finicky, brittle" parts, to paraphrase a New Girl reference: _"I'm not fully convinced I know how to [code]. I think I've just memorized a bunch of [patterns]!"_

It tends to become solid and robust as soon as you've become used to it.

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 19, 2025, 3:23pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/13 "2025-07-19T15:23:25Z")

</div>

I now have `#lang wire` working! See the [first `#lang wires` working implementation! commit in my github repo](https://github.com/dandrake/wires-racket-dsl/commit/7da07545a5a514eb0d505fb254doe100c69d94962).

I have a bunch of feedback for the Racket team about all this, which I'll put in another post.

For the moment, though, I'm very grateful to everyone here for your patience and help -- @bakgatviooldoos , @shhyou , @benknoble .

> [@bakgatviooldoos](#):
>
> As some interesting reading in this vein--making DSLs simple--see [#langs that Fit In Your Head – Terminally Undead](https://countvajhula.com/2024/12/09/langs-that-fit-in-your-head/), by @countvajhula.

That looks good. I skimmed it and will read it more carefully later!

---

<div class="post-metadata">

**Author:** ![bakgatviooldoos](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/bakgatviooldoos/32/1381_2.png) [@bakgatviooldoos](https://racket.discourse.group/u/bakgatviooldoos)\
**Post date:** [July 19, 2025, 9:48pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/14 "2025-07-19T21:48:06Z")

</div>

As another nice reference, I was browsing @soegaard's [Racket Stories](https://racket-stories.com/)  
aggregator, and came across this guide, [How to Hash-Lang](https://www.williamjbowman.com/tmp/how-to-hashlang/), by @wilbowma, although I have not yet read through its entirety.

---

<div class="post-metadata">

**Author:** ![ddrake](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/ddrake/32/2353_2.png) [@ddrake](https://racket.discourse.group/u/ddrake)\
**Post date:** [July 19, 2025, 11:11pm UTC](https://racket.discourse.group/t/confused-about-setup-for-reader-and-expander-for-simple-dsl/3859/15 "2025-07-19T23:11:13Z")

</div>

Based on the first paragraphs of How to Hash Lang, that's exactly what I want. I'll read it and see if it delivers on what I think it's promising...
