5Racket Internalizes Extra-Linguistic Mechanisms

While many programming problems originate in a “real” world, program
development is also a problem domain. As such, tools that support
programming deserve a language of their own. Compiler writers take this
idea seriously; for example, Dybvig and his group have developed a
language for stating compilers as nano-scale transformations and used it
for both educational (Sarkar et al.2005) and commercial
purposes (Keep and Dybvig2013). When it comes to program development or
program execution, however, IDEs resort to mechanisms from the surrounding
operating system. They force programmers to develop programs in project
contexts, delegate program execution to operating systems, and use
external tools to inspect programs and their execution states.
Racket’s focus on languages as the key to problem solving points to the
alternative solution of turning these extra-linguistic mechanisms into
linguistic constructs (Flatt et al.1999).This guideline does
not mean that linguistic constructs ought to replace all
tools. It merely means that the creation of every tool must raise the
question whether it should be a tool or a part of the language. Some tasks
are best left to tools, others are better realized as extensions of the
language. Finding a good balance is a difficult problem. For example, the
creator of a Racket library or a large system may very well wish to think
in terms of projects, but a novice student must never have to say “create
project” before writing and running a “hello world” program.

To appreciate this idea, consider the original problem of building a pedagogic
IDE for novice programmers. Clearly, the emphasis on pedagogy and novices
prohibits the use of “projects;” students should be able to type in programs
without any knowledge about computers and to run these programs without leaving
the IDE they use. By implication, the IDE runs student programs under its
control. Students make mistakes, though, and one common mistake is to launch a
diverging program, that is, a program that consumes unbounded amounts of time,
memory, or other resources (e.g., file ports, database handles, network
connections; sometimes the access may be via instructor-provided libraries). Similarly,
novices want to find mistakes in programs, meaning their instructors want to
show them how to step through a program’s execution. Finally, when a student
submits a program to some homework server, this program must run in a security
context that prohibits it from inspecting other students’ solutions, attacking
the server, and so on.

A close look at these requirements immediately suggests several areas of
concern. Due to its design feedback loop, Racket includes the following
external mechanisms as constructs at the moment:
inspectors, which establish a hierarchy of access rights;
threads that can be shut down from the outside;
sandboxes, which restrict access to services;
custodians, which manage file handles, sockets, and database connections;
eventspaces, which deal with GUI resources and events;
and several more.
The remainder of this section sketches two of these capabilities—inspectors
and custodians—and how providing them inside the language
provides fine-grained control over inspection and resources.

Figure 8 and~figure 9 demonstrate how Racket turns program
inspection into a linguistic construct. Ordinarily, a Racket structure
declaration like the one for s in figure 9
defines several functions: a constructor s, a field accessor s-fld,
and a predicate s?. Unless a module exports the field accessor, instances
of s are opaque to other modules in the system, i.e., other
modules cannot view, access, or mutate the content of field fld in an
instance. For example, dynamically loading module inspected.rkt from
figure 9, retrieving instance-of-s, and printing it would
reveal no information:

When the Racket IDE dynamically loads and evaluates a student program, however,
it needs to have access to structure information for printing, stepping, and
debugging.

To address these needs, Racket evaluates modules under a hierarchy of
inspectors. If two modules run under the same inspector or incomparable
inspectors in the hierarchy, they cannot view, access, or mutate each
others structures unless they explicitly grant these rights via provides
of the respective functions. In contrast, if module A runs under the
control of inspector i and another module B runs under the
control of an inspector j that is below i, A can
inspect B’s structures—whether B grants these rights or
not.

Consider the module in figure 8, which concretely illustrates how
inspectors work. The module creates a reference to the current inspector, that is,
the inspector under whose supervision it executes. It then makes another
inspector; the new one is below make-inspector’s
argument, which is the module’s current inspector. The module then uses
parameterize to set the value of the current-inspector to this newly
created inspector for the duration of the evaluation of

As a result, the value of v is a transparent instance of s,
which is defined in inspected.rkt but exported without access methods. Hence,
when inspector.rkt is loaded into the read-eval-print loop of DrRacket,
v prints as (s42).

Figure 10 presents an example of resource administration, another
operating-system service turned into a Racket construct. It displays the
essence of the launch-many-worlds function, which is used to run students’
distributed programs (Felleisen et al.2009)
in parallel. The function
consumes an arbitrary number of thunks and runs them in parallel until all of
them have produced a proper value or one of them has signaled an
exception. Since the function itself consumes resources, it uses two
custodians: its caller’s—to manage the resources of the given thunks—and a new
one—to manage its own resources, mostly threads. If any of these thunks raise an
exception, the latter custodian is shut down and all of
launch-many-world’s resources are released. For a more sophisticated pattern of
killing threads safely, see Flatt and Findler’s work on kill
safety (Flatt and Findler2004).

Finally, Racket also internalizes other aspects of its context. Dating back to
the beginning, Racket programs can programmatically link
modules (Flatt and Felleisen1998) and classes (Flatt et al.1998). In conventional
languages, programmers must resort to extra-linguistic tools to abstract over
such linguistic constructs; only ML-style languages and some scripting
languages make modules and classes programmable, too.