|
|
# Module Languages
|
|
|
|
|
|
When using the longhand `module` form for writing modules, the module
|
|
|
path that is specified after the new module’s name provides the initial
|
|
|
imports for the module. Since the initial-import module determines even
|
|
|
the most basic bindings that are available in a module’s body, such as
|
|
|
`require`, the initial import can be called a _module language_.
|
|
|
|
|
|
The most common module languages are `racket` or `racket/base`, but you
|
|
|
can define your own module language by defining a suitable module. For
|
|
|
example, using `provide` subforms like `all-from-out`, `except-out`, and
|
|
|
`rename-out`, you can add, remove, or rename bindings from `racket` to
|
|
|
produce a module language that is a variant of `racket`:
|
|
|
|
|
|
> +\[missing\] introduces the longhand `module` form.
|
|
|
|
|
|
```racket
|
|
|
> (module raquet racket
|
|
|
(provide (except-out (all-from-out racket) lambda)
|
|
|
(rename-out [lambda function])))
|
|
|
> (module score 'raquet
|
|
|
(map (function (points) (case points
|
|
|
[(0) "love"] [(1) "fifteen"]
|
|
|
[(2) "thirty"] [(3) "forty"]))
|
|
|
(list 0 2)))
|
|
|
> (require 'score)
|
|
|
'("love" "thirty")
|
|
|
```
|
|
|
|
|
|
## 1. Implicit Form Bindings
|
|
|
|
|
|
If you try to remove too much from `racket` in defining your own module
|
|
|
language, then the resulting module will no longer work right as a
|
|
|
module language:
|
|
|
|
|
|
```racket
|
|
|
> (module just-lambda racket
|
|
|
(provide lambda))
|
|
|
> (module identity 'just-lambda
|
|
|
(lambda (x) x))
|
|
|
eval:2:0: module: no #%module-begin binding in the module's
|
|
|
language
|
|
|
in: (module identity (quote just-lambda) (lambda (x) x))
|
|
|
```
|
|
|
|
|
|
The `#%module-begin` form is an implicit form that wraps the body of a
|
|
|
module. It must be provided by a module that is to be used as module
|
|
|
language:
|
|
|
|
|
|
```racket
|
|
|
> (module just-lambda racket
|
|
|
(provide lambda #%module-begin))
|
|
|
> (module identity 'just-lambda
|
|
|
(lambda (x) x))
|
|
|
> (require 'identity)
|
|
|
#<procedure>
|
|
|
```
|
|
|
|
|
|
The other implicit forms provided by `racket/base` are `#%app` for
|
|
|
function calls, `#%datum` for literals, and `#%top` for identifiers that
|
|
|
have no binding:
|
|
|
|
|
|
```racket
|
|
|
> (module just-lambda racket
|
|
|
(provide lambda #%module-begin
|
|
|
; ten needs these, too:
|
|
|
#%app #%datum))
|
|
|
> (module ten 'just-lambda
|
|
|
((lambda (x) x) 10))
|
|
|
> (require 'ten)
|
|
|
10
|
|
|
```
|
|
|
|
|
|
Implicit forms such as `#%app` can be used explicitly in a module, but
|
|
|
they exist mainly to allow a module language to restrict or change the
|
|
|
meaning of implicit uses. For example, a `lambda-calculus` module
|
|
|
language might restrict functions to a single argument, restrict
|
|
|
function calls to supply a single argument, restrict the module body to
|
|
|
a single expression, disallow literals, and treat unbound identifiers as
|
|
|
uninterpreted symbols:
|
|
|
|
|
|
```racket
|
|
|
> (module lambda-calculus racket
|
|
|
(provide (rename-out [1-arg-lambda lambda]
|
|
|
[1-arg-app #%app]
|
|
|
[1-form-module-begin #%module-begin]
|
|
|
[no-literals #%datum]
|
|
|
[unbound-as-quoted #%top]))
|
|
|
(define-syntax-rule (1-arg-lambda (x) expr)
|
|
|
(lambda (x) expr))
|
|
|
(define-syntax-rule (1-arg-app e1 e2)
|
|
|
(#%app e1 e2))
|
|
|
(define-syntax-rule (1-form-module-begin e)
|
|
|
(#%module-begin e))
|
|
|
(define-syntax (no-literals stx)
|
|
|
(raise-syntax-error #f "no" stx))
|
|
|
(define-syntax-rule (unbound-as-quoted . id)
|
|
|
'id))
|
|
|
> (module ok 'lambda-calculus
|
|
|
((lambda (x) (x z))
|
|
|
(lambda (y) y)))
|
|
|
> (require 'ok)
|
|
|
'z
|
|
|
> (module not-ok 'lambda-calculus
|
|
|
(lambda (x y) x))
|
|
|
eval:4:0: lambda: use does not match pattern: (lambda (x)
|
|
|
expr)
|
|
|
in: (lambda (x y) x)
|
|
|
> (module not-ok 'lambda-calculus
|
|
|
(lambda (x) x)
|
|
|
(lambda (y) (y y)))
|
|
|
eval:5:0: #%module-begin: use does not match pattern:
|
|
|
(#%module-begin e)
|
|
|
in: (#%module-begin (lambda (x) x) (lambda (y) (y y)))
|
|
|
> (module not-ok 'lambda-calculus
|
|
|
(lambda (x) (x x x)))
|
|
|
eval:6:0: #%app: use does not match pattern: (#%app e1 e2)
|
|
|
in: (#%app x x x)
|
|
|
> (module not-ok 'lambda-calculus
|
|
|
10)
|
|
|
eval:7:0: #%datum: no
|
|
|
in: (#%datum . 10)
|
|
|
```
|
|
|
|
|
|
Module languages rarely redefine `#%app`, `#%datum`, and `#%top`, but
|
|
|
redefining `#%module-begin` is more frequently useful. For example, when
|
|
|
using modules to construct descriptions of HTML pages where a
|
|
|
description is exported from the module as `page`, an alternate
|
|
|
`#%module-begin` can help eliminate `provide` and quasiquoting
|
|
|
boilerplate, as in `"html.rkt"`:
|
|
|
|
|
|
`"html.rkt"`
|
|
|
```racket
|
|
|
#lang racket
|
|
|
(require racket/date)
|
|
|
|
|
|
(provide (except-out (all-from-out racket)
|
|
|
#%module-begin)
|
|
|
(rename-out [module-begin #%module-begin])
|
|
|
now)
|
|
|
|
|
|
(define-syntax-rule (module-begin expr ...)
|
|
|
(#%module-begin
|
|
|
(define page `(html expr ...))
|
|
|
(provide page)))
|
|
|
|
|
|
(define (now)
|
|
|
(parameterize ([date-display-format 'iso-8601])
|
|
|
(date->string (seconds->date (current-seconds)))))
|
|
|
```
|
|
|
|
|
|
Using the `"html.rkt"` module language, a simple web page can be
|
|
|
described without having to explicitly define or export `page` and
|
|
|
starting in `quasiquote`d mode instead of expression mode:
|
|
|
|
|
|
```racket
|
|
|
> (module lady-with-the-spinning-head "html.rkt"
|
|
|
(title "Queen of Diamonds")
|
|
|
(p "Updated: " ,(now)))
|
|
|
> (require 'lady-with-the-spinning-head)
|
|
|
> page
|
|
|
'(html (title "Queen of Diamonds") (p "Updated: " "2019-01-21"))
|
|
|
```
|
|
|
|
|
|
## 2. Using `#lang`` ``s-exp`
|
|
|
|
|
|
Implementing a language at the level of `#lang` is more complex than
|
|
|
declaring a single module, because `#lang` lets programmers control
|
|
|
several different facets of a language. The `s-exp` language, however,
|
|
|
acts as a kind of meta-language for using a module language with the
|
|
|
`#lang` shorthand:
|
|
|
|
|
|
```racket
|
|
|
#lang s-exp module-name
|
|
|
form ...
|
|
|
```
|
|
|
|
|
|
is the same as
|
|
|
|
|
|
```racket
|
|
|
(module name module-name
|
|
|
form ...)
|
|
|
```
|
|
|
|
|
|
where `name` is derived from the source file containing the `#lang`
|
|
|
program. The name `s-exp` is short for “S-expression,” which is a
|
|
|
traditional name for Racket’s reader-level lexical conventions:
|
|
|
parentheses, identifiers, numbers, double-quoted strings with certain
|
|
|
backslash escapes, and so on.
|
|
|
|
|
|
Using `#lang s-exp`, the `lady-with-the-spinning-head` example from
|
|
|
before can be written more compactly as:
|
|
|
|
|
|
```racket
|
|
|
#lang s-exp "html.rkt"
|
|
|
|
|
|
(title "Queen of Diamonds")
|
|
|
(p "Updated: " ,(now))
|
|
|
```
|
|
|
|
|
|
Later in this guide, \[missing\] explains how to define your own `#lang`
|
|
|
language, but first we explain how you can write reader-level extensions
|
|
|
to Racket.
|