You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/quad/qtest/mds/module-languages.md

204 lines
8.6 KiB
Markdown

6 years ago
# Module Languages
When using the longhand `module` form for writing modules, the module
path that is specified after the new modules name provides the initial
imports for the module. Since the initial-import module determines even
the most basic bindings that are available in a modules 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 Rackets 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.