17.1 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:
The module Form introduces the longhand module form.
> (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")
17.1.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:
> (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:
> (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:
> (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:
> (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"
#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 quasiquoted mode instead of expression mode:
> (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: " "2024-11-22"))
17.1.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:
#lang s-exp module-name form ...
is the same as
(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:
#lang s-exp "html.rkt" (title "Queen of Diamonds") (p "Updated: " ,(now))
Later in this guide, Defining new #lang Languages explains how to define your own #lang language, but first we explain how you can write reader-level extensions to Racket.