call methods but instead type commands that have to be parsed and
dynamically mapped to responses for
places and things. Similarly, saving
and loading a game requires inspecting and restoring the state of places
and things, which is partly a matter
of object serialization but also of setting variables to unmarshalled values
(or else using an indirection through
a dictionary for each reference from
one object to another).
Some programming languages
include constructs—such as over-
loading or laziness—that a clever
programmer can exploit to encode
a domain-specific language. The de-
sign of Racket addresses the problem
more directly; it gives programmers
tools to explicitly extend the pro-
gramming language with new syn-
tax. Some tasks require only a small
extension to the core language, while
others benefit from the creation of
an entirely new language. Racket
supports both ends of the spectrum,
and it does so in a way that allows a
smooth progression from one end to
the other. As a programmer’s needs
or ambitions grow for a particular
task, the programmer can take advan-
tage of ever more of Racket’s unified
framework for language extension
figure 1. Distribution of value for the iPhone based on Ken Kraemer’s research.
(aliases ; list of symbols
Desc ; string
transitive?)) ; Boolean
The text-adventure example presented here illustrates the progression from a simple embedding in
Racket to a separate domain-specific
language (including IDE support for
syntax coloring), explaining relevant
Racket details along the way; no prior knowledge of Racket is necessary.
Readers who prefer a more complete
introduction to the language should
consult The Racket Guide. 1
The example is a “toy” in multiple
senses of the world, but it is also a scale
model of industry practice. Most every
video-game developer uses a custom
language, including the Racket-based
language that is used to implement
content for the Uncharted video-game
series. 2 Evidently, when billions of entertainment dollars are on the line, the
choice of programming language matters—even to the point of creating new,
(name ; symbol
[state #:mutable] ; any value
actions)) ; list of verb–function pairs
(desc ; string
[things #:mutable] ; list of things
actions)) ; list of verb–function pairs
figure 2. example place definition.
(define meadow (place "You’re in a meadow."
(list (cons south
(lambda () desert)))))
figure 3. Registering game element definitions.
(define names (make-hash)) ; symbol to place/thing
(define elements (make-hash)) ; place/thing to symbol
(define (record-element! name val)
(define (name->element name) (hash-ref names name))
(define (element->name obj) (hash-ref elements obj))
Consequently, the complete implementation of the meadow is:
(define meadow (place ....)) ; as in Figure 2.
(record-element! 'meadow meadow)
the world in Plain Racket
Our text adventure game contains a
fixed set of places, such as a meadow,
house, or desert, and a fixed set of
things, such as a door, key, or flower.
The player navigates the world and interacts with things using commands
that are parsed as either one or two
words: a single verb (that is, an intransitive verb, since it does not have
a target object) such as help or look; or
a verb followed by the name of a thing
(that is, a transitive verb followed by
a noun) such as open door or get key.
Navigation words such as north or in
are treated as verbs. A user can save the
game using the save and load verbs,
which work everywhere and prompt
the user for a file name.
To implement a text-adventure game
in Racket, you would start by declaring
structure types for each of the three
game elements shown in Figure 1.
Racket is a dialect of Lisp and a descendant of Scheme, so its syntax uses
parentheses and a liberal grammar
of identifiers (for example, transitive? is an identifier). A semicolon
introduces a newline-terminated
comment. Square brackets are interchangeable with parentheses but are
used by convention in certain contexts, such as grouping a field name
with modifiers. The #:mutable modifier declares a field as mutable, since
fields are immutable by default.