Racket is an odd language.
The first thing everyone notices is the S-expressions. But that misses the actual most important thing to pay attention to first: the #lang
line at the top of the file.
#lang racket
(define (square x)
(* x x))
You can put almost anything you want there, as long as it's the name of a module. That module is then used to parse and compile the rest of the file. Racket, at its core, is a language for defining new programming languages, including decidedly un-Lispy languages.
One thing a #lang
implementation can define is which names are in scope by default in a module written in that language. In the code above, define
and *
are both built-in bindings provided by #lang racket
. But they're also defined in the racket/base
module like any other macro or function (or operator, or class, or what have you) would be. You could import them directly with (require racket/base)
. So how exactly does #lang racket
bring them into scope?
Prelude modules
When a #lang
reads a program, it converts the raw text of the file through whatever custom parser it wants until it produces an output S-expression that looks like this:
(module foo prelude-module
... body ...)
...where foo
is an arbitrary name for this new module, and prelude-module
is the name of some installed module that should be imported into this module to provide the initial bindings for the whole module body. The module body itself might request more modules with (require another-module)
, but that's only possible if prelude-module
brings the require
form into scope in the first place! Choosing a prelude-module
that doesn't export any way to require additional modules essentially creates a module that's disallowed at the language level from importing other modules. Some #lang
implementations, like #lang info
, use this trick to let them function more like configuration and data format languages than general purpose programming languages.
On naming
The Racket community doesn't have a good name for this pattern. I think that's a shame, personally. My choice of "prelude module" for this isn't sanctioned; you won't find it anywhere in the documentation. But I'm writing this in an idle hope that this might change someday. It's basically equivalent to how custom preludes in Haskell work, and I think it's a useful term to have.