Program result = read → expand → evaluate → print.
The reader for the language converts the surface notation of the source file into S-expressions and returns a a new module expression.
For this reason, every source file starts with a #lang line (pronounced hash-lang) that “boots” into the reader.
Modules can also contain other modules. These are called, unsurprisingly, submodules, and they are loaded independently of their enclosing module:
Every Racket source file consists of a single module expression at the top level. This is why in Racket lingo, a source file is also known as a module. For instance, these two Racket sources are roughly equivalent:
Question: in what way are they not equivalent?
The expander determines the semantics of the new module. It does this by supplying the initial set of bindings for the module expression returned by the reader, thereby determining the meaning of the identifiers within the S-expressions.
Question: why do the modules above behave differently?
The expander leaves behind an ordinary Racket program (known as the fully expanded program) which can be evaluated normally (by running it with racket).
Our goal in every language implementation: to use the reader and expander to convert our surface notation into the equivalent Racket program. In that sense, every Racket-implemented language is a source-to-source compiler.
Corollary: since every language ultimately compiles into a Racket program, your language can do anything Racket can do.
The reader and expander do their work in separate passes. How we divide the labor between them is discretionary.
A language can have multiple readers that target a single expander.
Question: why might this be valuable?
Or multiple expanders that target a single reader.
Question: why might this be valuable?
A language can be implemented using any #lang (as long as it adheres to a few necessary conventions).
Question: why might this be valuable?
Using #lang racket, write a program that behaves the same way as this Pollen program:
1 2 3 | #lang pollen Hello world ◊metas |
Result when run:
1 2 | Hello world '#hasheq((here-path . "hello.rkt")) |
And then at the REPL:
1 | > doc |
1 | "Hello world\n'#hasheq((here-path . \"hello.rkt\"))" |
All roads lead to #lang racket.