0% found this document useful (0 votes)
22 views10 pages

Lang Lib

The document discusses the design and capabilities of Racket, a programming language that extends the Scheme language with powerful language extension APIs. It emphasizes the ability to create new languages and domain-specific languages that can seamlessly integrate with Racket's existing features, including static semantics and optimization. The paper also highlights the implementation of Typed Racket, a statically typed sister language of Racket, which allows for gradual migration from untyped to typed programming while maintaining compatibility with existing modules.

Uploaded by

space.boba707
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views10 pages

Lang Lib

The document discusses the design and capabilities of Racket, a programming language that extends the Scheme language with powerful language extension APIs. It emphasizes the ability to create new languages and domain-specific languages that can seamlessly integrate with Racket's existing features, including static semantics and optimization. The paper also highlights the implementation of Typed Racket, a statically typed sister language of Racket, which allows for gradual migration from untyped to typed programming while maintaining compatibility with existing modules.

Uploaded by

space.boba707
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Languages as Libraries

Sam Tobin-Hochstadt Vincent St-Amour Ryan Culpepper Matthew Flatt Matthias Felleisen
Northeastern University Northeastern University University of Utah University of Utah Northeastern University

Abstract collectors and thread abstractions, that these platforms offer. Both
Programming language design benefits from constructs for extend- platforms also inspired language design projects that wanted to ex-
ing the syntax and semantics of a host language. While C’s string- periment with new paradigms and to exploit existing frameworks;
based macros empower programmers to introduce notational short- thus Clojure, a parallelism-oriented descendant of Lisp, and Scala,
hands, the parser-level macros of Lisp encourage experimentation a multi-paradigm relative of Java, target the JVM, while F# is built
with domain-specific languages. The Scheme programming lan- atop .NET. In all of these cases, however, the platform is only a
guage improves on Lisp with macros that respect lexical scope. target, not a tool for growing languages. As a result, design experi-
The design of Racket—a descendant of Scheme—goes even fur- ments on these platforms remain costly, labor-intensive projects.
ther with the introduction of a full-fledged interface to the static se- To follow Steele’s advice on growing a language (1998) re-
mantics of the language. A Racket extension programmer can thus quires more than a reusable virtual machine and its libraries;
add constructs that are indistinguishable from “native” notation, it demands an extensible host language that supports linguistic
large and complex embedded domain-specific languages, and even reuse (Krishnamurthi 2001). Thus, a derived language should be
optimizing transformations for the compiler backend. This power able to reuse the scoping mechanisms of the host language. Simi-
to experiment with language design has been used to create a series larly, if the host language offers namespace management for iden-
of sub-languages for programming with first-class classes and mod- tifiers, a language designer should have the freedom to lift that
ules, numerous languages for implementing the Racket system, and management into an experimental language.
the creation of a complete and fully integrated typed sister language Providing such freedoms to designers demands a range of ex-
to Racket’s untyped base language. tension mechanisms. A programmer may wish to manipulate the
This paper explains Racket’s language extension API via an im- surface syntax and the AST, interpose a new context-sensitive static
plementation of a small typed sister language. The new language semantics, and communicate the results of the static semantics to
provides a rich type system that accommodates the idioms of un- the backend. Best of all, this kind of work can proceed without any
typed Racket. Furthermore, modules in this typed language can changes to the host language, so that the resulting design is essen-
safely exchange values with untyped modules. Last but not least, tially a library that supplements the existing compiler.
the implementation includes a type-based optimizer that achieves In this paper, we present the Racket1 (Flatt and PLT 2010) plat-
promising speedups. Although these extensions are complex, their form, which combines a virtual machine and JIT compiler with
Racket implementation is just a library, like any other library, re- a programming language that supports extension mechanisms for
quiring no changes to the Racket implementation. all phases of language implementation. With Racket’s arsenal of
extension mechanisms, a programmer may change all aspects of
Categories and Subject Descriptors D.3.3 [Programming Lan- a language: lexicographic and parsed notation, the static seman-
guages]: Language Constructs and Features tics, module linking, and optimizations. The Racket development
team has exploited this extensibility for the construction of two
General Terms Languages, Design
dozen frequently used languages, e.g., language extensions for
classes (Flatt et al. 2006) and ML-style functors (Culpepper et
1. Growing Many Languages al. 2005; Flatt and Felleisen 1998), another for creating interac-
I need to design a language that can grow. — Guy Steele, 1998 tive web server applications (Krishnamurthi et al. 2007), two lan-
guages implementing logic programming2 and a lazy variant of
Virtual machines inspire language experimentation. For example, Racket (Barzilay and Clements 2005). This paper is itself a pro-
the Java Virtual Machine and the .NET CLR attracted many im- gram in Racket’s documentation language (Flatt et al. 2009).
plementors to port existing languages. Their goal was to benefit Racket derives its power from a carefully designed revision of a
from the rich set of libraries and runtime facilities, such as garbage Scheme-style macro systems. The key is to make language choice
specific to each module. That is, individual modules of a program
∗A preliminary version of this work appeared at the Workshop on Scheme can be implemented in different languages, where the language
and Functional Programming (Culpepper et al. 2007). This work has been implementation has complete control over the syntax and semantics
supported by the DARPA, Mozilla Foundation, NSERC, and NSF. of the module. The language implementation can reuse as much
of the base language as desired, and it can export as much of the
module’s internals as desired. In particular, languages can reuse
Racket’s macro facilities, which is supported with a mechanism for
Permission to make digital or hard copies of all or part of this work for personal or locally expanding macros into attributed ASTs.
classroom use is granted without fee provided that copies are not made or distributed
for profit or commercial advantage and that copies bear this notice and the full citation
on the first page. To copy otherwise, to republish, to post on servers or to redistribute
1 Formerly known as PLT Scheme.
to lists, requires prior specific permission and/or a fee.
PLDI’11, June 4–8, 2011, San Jose, California, USA. 2 Schelog (1993) http://docs.racket-lang.org/racklog
Copyright © 2011 ACM 978-1-4503-0663-8/11/06. . . $5.00 Datalog (2010) http://docs.racket-lang.org/datalog
For simplicity, we demonstrate the idea of true language ex- The do-10-times macro consumes a sequence of expressions
tensibility via a single example: the Typed Racket implementation. and produces code that runs these expressions 10 times. To this
Typed Racket (Tobin-Hochstadt and Felleisen 2008) is a statically end, it first decomposes its input, stx, with the syntax-parse
typed sister language of Racket that is designed to support the grad- form (Culpepper and Felleisen 2010), a pattern matcher designed
ual porting of untyped programs. Its type system accommodates the for implementing macros. The pattern requires a sequence of
idioms of Racket, its modules can exchange values with untyped subexpressions, indicated with ..., named body, each of which
modules, and it has a type-driven optimizer—all implemented as is constrained to be an expr. The resulting syntax is a for loop
plain Racket libraries. that contains the body expressions. Thanks to macro hygiene, if
The remainder of this paper starts with a description of Racket’s the bodys use the variable i, it is not interfered with by the use of
language extension facilities and the challenges posed by the Typed i in the for loop.
Racket extension. Following these background sections, the paper
shows how we use syntactic extension to introduce Typed Racket 2.2 Manipulating Syntax Objects
notation; how we splice the type checker into the tool chain; how Since syntax objects are Racket’s primary compile-time data struc-
we enable inter-language linking; and how we add realistic op- ture, roughly analagous to the ASTs of conventional languages,
timizing transformations via a library. Finally, we relate our ap- syntax objects come with a rich API.
proach to existing facilities for meta-programming with static se-
mantics as well as extensible compiler projects. Constructors and Accessors Besides with-syntax and #’ for
constructing and manipulating syntax objects, the remainder of the
paper also uses the following forms and functions:
2. Language Extension in Racket
• syntax->list converts a non-atomic syntax object to a list;
Racket provides a range of facilities for language extension. In
• free-identifier=? compares two identifiers to deter-
this section, we outline the most relevant background as well as
extension mechanisms provided with Racket. mine if they refer to the same binding;
• the #‘ and #, forms implement Lisp’s quasiquote and unquote
2.1 Macros for syntax object construction.
From Lisp (Steele Jr. 1994) and Scheme (Sperber et al. 2009), Syntax properties Racket macros can add out-of-band informa-
Racket takes hygienic macros as the basis of its syntactic exten- tion to syntax objects, e.g., source locations, dubbed syntax proper-
sion mechanisms. In Racket, macros are functions from syntax to ties. The syntax-property-put and syntax-property-
syntax, which are executed at compile time. During compilation, get procedures attach and retrieve arbitrary key-value pairs on
Racket’s macro expander recursively traverses the input syntax. syntax objects, which are preserved by the macro expander. Thus
When it reaches the use of a macro, it runs the associated function, syntactic extensions may communicate with each other without in-
the transformer, and continues by traversing the result. terfering with each other.
Macros may use arbitrary Racket libraries and primitives to
compute the result syntax. The following macro queries the system Local Expansion The local-expand procedure expands a
clock at compile time: syntax object to core Racket. This explicitly specified core lan-
(define-syntax (when-compiled stx) guage consists of approximately 20 primitive syntactic forms that
(with-syntax ([ct (current-seconds)]) implement Racket; see figure 1 for a subset of this grammar. Using
#’ct)) local-expand, a language extension may analyze an arbitrary
This macro computes, at compile time, the current time in seconds, expression, even if that expression uses macros. For example, the
and uses the with-syntax form to bind the identifier ct to following macro requires that its argument be a λ expression:
a syntax object representing this number. Syntax objects are the (define-syntax (only-λ stx)
ASTs of Racket, and contain syntactic data as well as metadata (syntax-parse stx
such as source location information. The #’ form (analogous to ’ [(_ arg:expr)
for lists) constructs syntax objects and can refer to identifiers bound (define c
by with-syntax in its context. In the following function, we use (local-expand #’arg ’expression ’()))
the when-compiled macro: (define k (first (syntax->list c)))
(define (how-long-ago?) (if (free-identifier=? #’#%plain-lambda k)
(- (current-seconds) (when-compiled))) c
Since the when-compiled form expands into the current date (error "not λ"))]))
in seconds at the time the form is compiled and since the value of The only-λ macro uses local-expand on the arg subexpres-
(current-seconds) continues to change, the value produced sion to fully expand it:
by how-long-ago? continually increases > (only-λ (λ (x) x))
> (how-long-ago?) #<procedure>
0 > (only-λ 7)
> (sleep 1) not λ
> (how-long-ago?) If we add a definition that makes function the same as λ, we
1 still get the correct behavior.
Most macros generate new expressions based on their input: > (only-λ (function (x) x))
(define-syntax (do-10-times stx) #<procedure>
(syntax-parse stx The only-λ macro can see through the use of function be-
[(do-10-times body:expr ...) cause of the use of local-expand.
#’(for ([i (in-range 10)])
body ...)])) 2.3 Modules and Languages
> (do-10-times (display "*") (display "#")) Racket provides a first-order module system that supports import
*#*#*#*#*#*#*#*#*#*# and export of language extensions (Flatt 2002). For the purposes
mod-form = expr 3. Typed Racket as a Library
| (#%provide provide-spec)
Typed Racket combines a type system for enriching Racket mod-
| (define-values (id) expr)
ules with sound type information and a mechanism for linking
| ...
typed and untyped modules. Its implementation not only benefits
from, but demands, as much linguistic reuse as possible. After all,
expr = id
Typed Racket must implement the same semantics as Racket plus
| (#%plain-lambda (id ...)
purely syntactic type checking; there is no other way to provide
expr ...+)
a smooth migration path that turns Racket programs into Typed
| (if expr expr expr)
Racket programs on a module-by-module basis. And the best way
| (quote datum)
to implement the same semantics is to share the compiler.
| (#%plain-app expr ...+)
At the same time, linguistic reuse poses novel challenges in
| ...
addition to those faced by every implementer of a typed language.
In this section, we explain these challenges with examples; in
Figure 1: Racket’s core forms (abbreviated) the remainder of the paper we explain our solutions. As for the
particular challenges, we show how to:

1. annotate bindings with type specifications;


of this paper, two aspects of the module system are crucial. First, 2. check type correctness in a context-sensitive fashion;
modules can export static bindings, such as macros, as well as value
bindings, such as procedures, without requiring clients to distin- 3. type check programs written in an extensible language;
guish between them. Thus, value bindings can be replaced with 4. integrate type checking with separate compilation of modules;
static bindings without breaking clients. Second, each module is
compiled with a fresh store. That is, mutations to state created dur- 5. provide safe interaction with untyped modules; and
ing one compilation do not affect the results of other compilations. 6. optimize programs based on type information.
Every module specifies—in the first line of the module—the
language it is written in. For example,
#lang racket 3.1 Layering Types on an Untyped Language
specifies racket as the module’s language, and Consider this trivial Typed Racket program:
#lang datalog #lang typed/racket
specifies datalog instead, a language extension with different (define: x : Number 3)
lexical syntax, static and dynamic semantics from plain Racket. It specifies that x has type Number. To implement define:,
For the purposes of this paper, a language L is a library that Typed Racket reuses the plain define provided by Racket. Thus
provides two linguistic features: we must associate the type declaration for x out-of-band.
Further, although modern languages come with a wide variety
• a set of bindings for L, including both syntactic forms such of syntactic forms, most can be reduced to simpler forms via rewrite
as define and values such as +, which constitute the base rules implemented as macros, e.g.:
environment of modules written in the language, and (define-syntax (let: stx)
• a binding named #%module-begin, which is used to imple- (syntax-parse stx
ment the whole-module semantics of L. [(let ([x:id : T rhs:expr]) body:expr)
#’((λ: ([x : T]) body) rhs)]))
The #%module-begin form is applied to the entire module For Typed Racket, these rewriting rules must preserve the specified
before the macro expander takes over. For example, here is the type information without interfering with the implementation of the
#%module-begin form for the count language: typechecker.
(define-syntax (#%module-begin stx) These are two instances of Typed Racket’s linguistic reuse.
(syntax-parse stx This linguistic reuse is comprehensive through all layers of Typed
[(#%module-begin body ...) Racket. Modules in Typed Racket are simply Racket modules. The
#‘(#%plain-module-begin same is true of functions and variables, which map directly to their
(printf "Found ∼a expressions." Racket equivalents. At the intermediate level, Typed Racket reuses
#,(length Racket’s binding forms, such as define and λ, with additions for
(syntax->list the specification of types as described above. At the lowest level,
#’(body ...)))) runtime values are shared between Racket and Typed Racket.
body ...)])) Returning to our example, the plain Racket definition form is:
The #%plain-module-begin form is the base module wrap- (define x 3)
per, adding no new static semantics. There is no place in such a definition for a type annotation. Hence,
When this language is used, it prints the number of top-level the macro must store the type information out-of-band. Fortunately,
expressions in the program, then runs the program as usual. For syntax properties allow for define: to record precisely such
example, consider the following module written in count: additional information:
#lang count (define-syntax (define: stx)
(printf "*∼a" (+ 1 2)) (syntax-parse stx
(printf "*∼a" (- 4 3)) [(define: name:id : ty rhs:expr)
When run, this module prints: (with-syntax
Found 2 expressions.*3*1. ([ann-name
The #%module-begin language mechanism allows a lan- (syntax-property-put
guage author to implement arbitrary new whole-module static se- #’name ’type-annotation #’ty)])
mantics using macro rewriting. #’(define ann-name rhs))]))
Here, ann-name is the original name, but with a syntax property #lang typed/racket
indicating that it is annotated with the type ty. With this implemen- (require/typed racket/file
tation, later stages of processing can read the type annotation from [file->lines (Path -> (Listof String))])
the binding, but the type annotation does not affect the behavior of (file->lines "/etc/passwd")
Racket’s define, allowing reuse as desired. Second, untyped modules can import typed bindings:
#lang racket ;; client
3.2 Challenges (require server)
Next we turn to the challenges of other processing phases. (add-5 12) ;; safe use
(add-5 "bad") ;; unsafe use
Context-sensitive Checking Type checking is inherently context- Typed Racket must protect its soundness invariants and check value
sensitive. For example, this Typed Racket program: flow across the boundary between typed and untyped programs. It
#lang typed/racket must also avoid, however, imposing unnecessary dynamic checks
(: f (Number -> Number)) between typed modules.
(define (f z) (sqrt (* 2 z)))
(f 7) Optimizing with Type Information Type information enables a
relies on contextual information about the type of f when check- wide variety of optimizations. Tag checking, ubiquitous in untyped
ing the function application (f 7). Tracking this information de- Racket programs, is unnecessary in typed programs. For example,
mands an implementation that typechecks the entire module, rather this program need not check that the argument to first is a pair:
than just the syntax available at a particular program point; in other #lang typed/racket
words, it is a whole-module analysis. (: p : (List Number Number Number))
(define p (list 1 2 3))
Checking an Extended Language Typed Racket programmers (first p)
expect to use the numerous libraries provided by Racket, many Of course, with type information, more significant optimizations
of which provide syntactic abstractions. For example, this module are also possible. The following loop is transformed into one that
uses match, a syntactic form implemented in a library written in performs only machine-level floating-point computation:
plain Racket, rather than a primitive form as in ML or Haskell, but #lang typed/racket
nonetheless indistinguishable from a language primitive: (: count : Float-Complex -> Integer)
#lang racket (define (count f)
(match (list 1 2 3) (let loop ([f f])
[(list x y z) (if (< (magnitude f) 0.001)
(+ x y z)]) 0
Naturally, Typed Racket programmers want to reuse such conve- (add1 (loop (/ f 2.0+2.0i))))))
nient libraries, ideally without modifications, so that the above be- In particular, the type information should assist the compiler back-
comes a valid Typed Racket module by using typed/racket. end with the treatment of complex and floating-point numbers.
Therefore, we must either extend our typechecker to handle
match, or translate match into a simpler form that the type- 4. A Single-Module Typechecker
checker understands. The former solution works for existing lan-
guage extensions, but in Racket, programmers can write new lan- This section presents the single-module core of a simply-typed
guage extensions at any time, meaning that Typed Racket cannot version of Typed Racket. It concludes with an explanation of how
possibly contain a catalog of all of them. to scale the system to the full typechecker of Typed Racket.
Supporting Modular Programs Racket programs consist of mul- 4.1 An Example
tiple modules; Typed Racket therefore supports modules as well. Assume our language is available from the simple-type library.
The types of bindings defined in one module must be accessible in Thus, we can write modules like the following:
other typed modules without the need to repeat type declarations. #lang simple-type
For example: (define x : Integer 1)
#lang typed/racket ;; module server (define y : Integer 2)
(: add-5 : Integer -> Integer) (define (f [z : Integer]) : Integer
(define (add-5 x) (+ x 5)) (* x (+ y z)))
;; export ‘add-5’ from the module Such a module is first fully expanded to the appropriate core
(provide add-5) forms and then typechecked. Thus, type-incorrect definitions and
expressions signal compile-time errors.
#lang typed/racket ;; module client (define w : Integer 3.7)
(require server) typecheck: wrong type in: 3.7
(add-5 7) ;; type checks correctly Modules with type errors are not executable.
Because modules are compiled separately, the server module
is compiled before the client module, but must communicate the 4.2 Wiring Up the Typechecker
static information about the type of add-5 to client. Therefore,
compiling a Typed Racket module must produce a persistent record Typechecking a module is a context-sensitive process and therefore
of the types of exported bindings. demands a whole-module analysis via #%module-begin.
Since Racket provides a rich mechanism for syntactic exten-
Integrating with Untyped Modules Racket comes with hundreds sion, many important language features are implemented as lan-
of thousands of lines of libraries, almost all of which are written guage extensions. Major examples are pattern matching, keyword
without types. Effective use of Typed Racket, therefore, requires arguments, and even simple conditional forms such as case and
interoperation with untyped modules. Typed Racket supports such cond. Providing appropriate type rules for every syntactic exten-
interoperation in both directions. First, typed modules can import sion, however, is clearly impossible, because programmers can in-
untyped bindings by specifying their types: vent new extensions at any time. Instead, we consider only the
(define-syntax (#%module-begin stx)
(syntax-parse stx
[(_ forms ...)
(with-syntax ([(_ core-forms ...)
(local-expand #’(#%plain-module-begin forms ...) ’module-begin ’())])
(for-each typecheck (syntax->list #’(core-forms ...)))
#’(#%plain-module-begin core-forms ...))]))

Figure 2: The Top-level Driver

(define (typecheck t [check #f])


(define the-type
(syntax-parse t
[v:identifier (lookup-type #’v)]
[(quote n:number) (cond [(exact-integer? (syntax-e #’n)) IntT]
[(flonum? (syntax-e #’n)) FloatT]
[else NumberT])]
[(if e1 e2 e3)
(typecheck #’e1 BooleanT)
(unless (equal? (typecheck #’e2) (typecheck #’e3))
(type-error "if branches must agree"))
(typecheck #’e3)]
[(#%plain-lambda formals body:expr)
(define formal-types (map type-of (syntax->list #’formals)))
(for-each add-type! (syntax->list #’formals) formal-types)
(make-fun-type formal-types (typecheck #’body))]
[(#%plain-app op . args)
(define argtys (map typecheck (syntax->list #’args)))
(match (typecheck #’op)
[(struct fun-type (formals ret))
(unless (and (= (length argtys) (length formals)) (andmap subtype argtys formals))
(type-error "wrong argument types" t))
ret]
[t (type-error "not a function type" #’op)])]
[(define-values (id) rhs)
(add-type! #’id (type-of #’id)) (typecheck #’rhs (type-of #’id))]))
(when (and check (not (subtype the-type check))) (type-error "wrong type" t))
the-type)
;; get the type of a binding
(define (type-of id)
(unless (syntax-property-get id ’type) (type-error "untyped variable" id))
(parse-type (syntax-property-get id ’type)))

Figure 3: The Typechecker

small fixed set of core forms of figure 1, and reduce all other forms fiers that the language provides, such as +, as well as the initial
to these before type checking. type names. Finally, we provide the define and λ binding forms
Given an implementation of the typechecker, we must connect it that attach the appropriate syntax properties for type annotations to
to the program so that it receives appropriate fully-expanded syntax the bound variables, as described in section 2.1.
objects as input. The basic driver is given in figure 2. The driver
is straightforward, performing only two functions. Once we have 4.3 Typechecking Syntax
fully expanded the body of the module, we typecheck each form in
turn. The typechecker raises an error if it encounters an untypable Figure 1 displays the grammar for our simple language. It is a sub-
form. Finally, we construct the output module from new core forms, set of the core forms of full Racket. Modules consist of a sequence
thus avoiding a re-expansion of the input. of mod-forms, which are either expressions or definitions.
The strategy of reducing syntactic sugar to core forms is com- Figure 3 specifies the typechecker for this core language. The
mon in many other languages, and even specified in the standards typecheck function takes a term and an optional result type.
for ML (Milner et al. 1997) and Haskell (Marlow 2010). In a lan- Each clause in the syntax-parse expression considers one of
guage with syntactic extension, we need more sophisticated sup- the core forms described in figure 1.
port from the system to implement this strategy, and that support is Two aspects of the typechecker are distinctive. First, the type en-
provided in Racket by local-expand. vironment uses a mutable table mapping identifiers to types based
For successful typechecking, we must also provide an initial en- on their binding; the table is accessed with lookup-type and up-
vironment. The initial environment specifies types for any identi- dated with add-type!. Shadowing is impossible because identi-
fiers in fully-expanded Racket programs are unique with respect to
the entire program. We update the type environment in the clauses (define-syntax (require/typed stx)
for both #%plain-lambda and define-values. Using an (syntax-parse stx
identifier-keyed table allows reuse of the Racket binding structure [(_ module [id ty])
without having to reimplement variable renaming or environments. #‘(begin-ignored
The second distinctive feature of the typechecker is the type- ; Stage 1
of function. It reads the syntax properties attached by forms such (require (only-in module
as define: in section 3.1 with a known key to determine the type [id unsafe-id]))
the user has added to each binding position. ; Stage 2
(begin-for-syntax
(add-type! #’id (parse-type #’ty)))
4.4 Scaling to Typed Racket ; Stage 3
While this module-level typechecker is simple, the full implemen- (define id
tation for Typed Racket employs the same strategy. The impor- (contract
tant differences concern mutual recursion and complex definition #,(type->contract
forms. Mutual recursion is implemented with a two-pass type- (parse-type #’ty))
checker: the first pass collects definitions with their types, and unsafe-id
the second pass checks individual expressions in this type context. (quote module)
Complex declarations, such as the definition of new types, are also ’typed-module)))]))
handled in the first pass. They are recognized by the typechecker,
and the appropriate bindings and types are added to the relevant Figure 4: Import of Untyped Code
environments.
Of course, the Typed Racket type system is much more com-
plex (Strickland et al. 2009; Tobin-Hochstadt and Felleisen 2010) 6. Safe Cross-Module Integration
than the one we have implemented here, but that complexity does A module in Typed Racket should have access to the large col-
not require modifications to the structure of the implementation—it lection of untyped libraries in Racket. Conversely, our intention
is encapsulated in the behavior of typecheck on the core forms. to support gradual refactoring of untyped into typed systems de-
mands that typed modules can export bindings to untyped mod-
ules. However, untyped programs are potentially dangerous to the
5. Modular Typed Programs invariants of typed modules. To protect these invariants, we auto-
The typechecker in section 4 deals only with individual modules. matically generate run-time contracts from the types of imported
To deal with multiple modules, we must both propagate type infor- and exported bindings (Tobin-Hochstadt and Felleisen 2006).
mation between typed modules, and also persist type information However, a large library of untyped modules is useless if each
in compiled code to support separate compilation. must be modified to work with typed modules. Therefore, our im-
For the first point, we reuse some Racket infrastruture. Names- plementation must automatically wrap imports from untyped code,
pace management in a modular language is a complex problem, and and protect exports to untyped code, without requiring changes to
one that Racket already solves. In particular, identifiers in Racket untyped modules. Further, communication between typed modules
are given globally fresh names that are stable across modules dur- should not involve extra contract checks, since these invariants are
ing the expansion process. Since our type environment is keyed by enforced statically.
identifiers, type environment lookup reuses and respects Racket’s
scoping. An identifier imported from one module maintains its 6.1 Imports from Untyped Modules
identity in the importing module, and therefore the typechecker is Typed Racket requires the programmer to specify the types of
able to look up the appropriate type for the binding. imports from untyped modules:
The second step is to maintain the type environment across com- (require/typed file/md5
pilations. Since each module is compiled in a separate and fresh [md5 (Bytes -> Bytes)])
store, mutations to the type environment do not persist between This specification imports the md5 procedure, which computes the
compilations. Therefore, Typed Racket must incorporate the type MD5 hash of a byte sequence. The procedure can be used in typed
environment into the residual program, because that is the only per- code with the specified type, and the type is converted to a con-
sistent result of compilation. Our strategy — due to Flatt (2002) — tract and attached to the procedure on import. This translation and
is to include code in the resulting module that populates the type interposition is implemented in the require/typed form; see
environment every time the module is required. figure 4 for an implementation for our simple-type language.
In our example system, we implement this by adding a single The implementation of require/typed works in three
rewriting pass to the #%module-begin form for Typed Racket. stages. Stage 1 imports the specified identifier, id from mod-
Expressions and definitions are left alone; an appropriate compile- ule under the new name unsafe-id. Stage 2 parses and adds
time declaration is added for each export: the specified type to the table of types with add-type!. Finally,
(provide n) ; The original export stage 3 defines the identifier id as a wrapper around unsafe-id.
; ==> (is rewritten into) The wrapper is a generated contract and establishes a dynamically
(with-syntax ([t (serialize (type-of #’n))]) enforced agreement between the original module (named module)
#‘(begin and the typed module (with the placeholder name).3 The entire out-
(#%provide n) ; The core export form put is wrapped in begin-ignored so that the type checker does
(begin-for-syntax not process this meta-information.
(add-type! #’n t)))) In our example, we would now be able to use the md5 function
The resulting code uses #%provide to maintain the original ex- in typed code according to the specified type, getting a static type
port. The type declaration is wrapped in begin-for-syntax, error if md5 is applied to a number, for example. Conversely, if the
meaning that it is executed at compile time to declare that n is
mapped to the serialization of its type in the type environment. 3 In practice, type checking renders the domain contract superfluous.
file/md5 library fails to return a byte string value, a dynamic as an indicator, without the possibility that untyped code might be
contract error is produced, avoiding the possibility that the typed able to deceive the typechecker.
module might end up with a value that it did not expect.
6.3 Scaling to Typed Racket
6.2 Exports to Untyped Modules The full implementation of module integration in Typed Racket
follows the strategy outlined. The major complication is the export
Unlike imports into a typed module, exports from such a module of macros and other static information from typed modules. Since
pose a serious problem because the Typed Racket language imple- macros from typed modules can refer to internal identifiers not
mentation is not necessarily in control of the use site. After all, an protected by contracts, expanding such macros in untyped modules
exported identifier may be used in both typed and untyped contexts. could potentially allow untyped modules to violate the invariants of
Since typed modules statically verify that uses of typed identifiers typed modules. Therefore, Typed Racket currently prevents macros
accord with their types, no contracts are necessary for exports to defined in typed modules from escaping into untyped modules.
such modules. In contrast, exports from typed to untyped modules
require the insertion of dynamic checks to ensure type safety.
To implement this behavior without cloning every module, we
7. Optimization via Rewriting
adopt a novel two-stage module compilation strategy. First, each Source-to-source transformations can express large classes of opti-
export is replaced with an indirection that chooses whether to ref- mizations, which makes it possible to apply them in the front end
erence the contracted or plain version of the exported binding. of the compiler. With the right language extension mechanisms, we
Second, the compilation of typed modules sets a flag inside the can express these transformations as libraries and use them to build
#%module-begin transformer before expansion of the module’s competitive optimizers.
contents; the exported indirections choose which version to ref-
erence based on this flag. Since each module is compiled with a 7.1 Compiler architecture
fresh state, this flag is only set during the compilation of typed Since Typed Racket is built as a language extension of Racket and
modules—untyped modules have no way to access it. Therefore, the Racket compiler is for an untyped language, Typed Racket
during the compilation of untyped modules, the export indirections has to apply typed optimizations before handing programs to the
correctly choose the contract-protected version of bindings. The Racket compiler. In contrast, compilers for typed languages can
compilation of typed modules, in contrast, see the set version of keep track of types across all phases in the compiler and use
the flag and are able to use the uncontracted versions of bindings. them to guide optimizations. Hence, Typed Racket features a type-
driven optimization pass after typechecking. This optimization pass
Implementation Exported identifiers are rewritten in the same transforms the code that the front end of Typed Racket generates,
stages as imported identifiers. Since this rewriting occurs after using the validated and still accessible type information.
typechecking, however, it is performed by the #%module-begin To support realistic optimizers as libraries, the host language
form, just as declarations are added to exports: must provide ways for language extensions to communicate their
(#%provide n) ; An export of n results to the compiler’s backend. As part of its language exten-
; ==> (is rewritten to) sion features, Racket exposes unsafe type-specialized primitives.4
#‘(begin For instance, the unsafe-fl+ primitive adds two floating-point
... the declaration from section 5 ... numbers, but has undefined behavior when applied to anything
; Stage 1 else. These type-specialized primitives are more efficient than their
(define defensive-n generic equivalents; not only do these primitives avoid the run-
(contract time dispatch of generic operations, they also serve as signals to
#,(type->contract (typecheck #’n)) the Racket code generator to guide its unboxing optimizations.
n ’typed-module ’untyped-module)) Typed Racket’s optimizer generates code that uses these prim-
; Stage 2 itives. In figure 5, we show an excerpt from the optimizer that
(define-syntax (export-n stx) specializes floating-point operations using rewrite rules; the opti-
(if (unbox typed-context?) mizer rewrites uses of generic arithmetic operations on floating-
#’n #’defensive-n)) point numbers to specialized operations.
; Stage 3
(provide (rename-out [export-n n]))) 7.2 Scaling to Typed Racket
First, we define a defensive version of the identifier n, which Typed Racket uses the same techniques as the simple optimizer pre-
uses a contract generated from the type of n. Second, we de- sented here, but applies a wider range of optimizations. It supports a
fine an export-n version of the identifier n, which selects be- number of floating-point specialization transformations, eliminates
tween n and defensive-n depenending on the value of typed- tag-checking made redundant by the typechecker and performs ar-
context?. Third, we provide export-n under the name n, ity raising on functions with complex number arguments.
making the indirection transparent to clients of the typed module.
The second part comes in the definition of the language: 7.3 Results
(define-syntax (#%module-begin stx) Untyped Racket is already competitive among optimizing Scheme
(set-box! typed-context? #t) compilers. The addition of a type-driven optimizer makes a notice-
... check and transform stx, as in figure 2 ... able difference and makes it an even more serious contender.
... rewrite provides, as above ...) We show the impact of our optimizer on micro-benchmarks
The initial flag setting means that the expansion of the module, and taken from the Gabriel (1985) and Larceny (Clinger and Hansen
in particular the expansion of the indirections imported from other 1994) benchmark suites and the Computer Language Benchmark
typed modules, see the typed-context? flag as set to #t. Game,5 as well as on large benchmarks: the pseudoknot (Hartel
Finally, because the typed-context? flag is accessible only
4 Initially
these primitives were provided because some programmers
from the implementation of the simple-type language, it is
simple to verify that the flag is only set to #t in the #%module- wanted to hand-optimize code.
begin form. Therefore, the implementation can rely on this flag 5 http://shootout.alioth.debian.org
(define (optimize t)
(syntax-parse t
[(#%plain-app op:id e1:expr e2:expr)
(with-syntax ([new-op (if (and (equal? FloatT (type-of #’e1))
(equal? FloatT (type-of #’e2)))
(cond [(free-identifier=? #’op #’+) #’unsafe-fl+]
[(free-identifier=? #’op #’-) #’unsafe-fl-]
[else #’op])
#’op)])
#‘(#%plain-app new-op #,(optimize #’e1) #,(optimize #’e2)))]
... structurally recur on the other forms ...))

Figure 5: The Optimizer

2.5
Normalized time

2
Racket
1.5 Typed Racket
Bigloo
1
Larceny
0.5 Gambit

0
cpstack dderiv deriv div fft graphs lattice maze mazefun nfa nqueens paraffins tak takl triangle

Figure 6: Results on the Gabriel and Larceny benchmarks (smaller is better)

1.5 et al. 1996) floating-point benchmark, a ray tracer, an industrial


strength FFT and the implementation of two purely functional data
Normalized time

1
structures (Prashanth and Tobin-Hochstadt 2010). Each benchmark
0.5 Racket comes in two versions: the original version and a translation to
Typed Typed Racket. The typed versions have type annotations and extra
0 Racket predicates where required to typecheck the program.
nbody

spectralnorm
fannkuch

fasta

k-nucleotide

regex-dna
mandelbrot

rev.comp.
binarytrees

pidigits

The typed version runs in Racket 5.0.2 using our type-driven op-
timizer and the untyped version in Racket 5.0.2, Gambit 4.6.0 (Fee-
ley and Miller 1990), Larceny 0.97 (Clinger and Hansen 1994)
and Bigloo 3.5a (Serrano and Weis 1995), using the highest safe
optimization settings in each case. Benchmarks from the Com-
Figure 7: Results on the Computer Language Benchmark Game puter Language Benchmarks Game and our sample applications
(smaller is better) use Racket-specific features and cannot be measured with other
Scheme compilers. Bigloo fails to compile the cpstack and pseu-
1.5
Normalized time

doknot benchmarks. All our results are the average of 20 runs on a


Racket Dell Optiplex GX270 running GNU/Linux with 1GB of memory.
1 These benchmarks fall into two categories: benchmarks where
Typed Racket
Racket is already competitive with other well-known optimizing
Larceny
0.5 Scheme compilers, and benchmarks where Racket does not per-
Gambit
form as well. In some cases where Racket is slower than the compe-
0 tition, Typed Racket’s optimizer helps bridge the gap. For instance,
it is responsible for a 33% speedup on the fft benchmark and a
123% speedup on pseudoknot. The large applications benefit even
Figure 8: Results on pseudoknot (smaller is better)
more from our optimizer than the microbenchmarks.
The results presented in this section demonstrate (a) that the
Normalized time

1.5
Racket compiler and runtime are already competitive with leading
1 Racket compilers for Scheme, and (b) that Typed Racket’s optimizer, writ-
Typed ten entirely as a library, nonetheless provides noticeable speedups
0.5
Racket on both widely-used benchmarks and existing Racket programs.
0
ray tracer fft banker's queue leftist heap

Figure 9: Results on large benchmarks (smaller is better)


8. Related Work higher level of abstraction means that the compiler can be mod-
While many individual aspects of our extensible language have ified without breaking existing extensions, and extensions need
long histories, some are novel and so is the combination as a whole. not depend on the specialized representations and analyses in the
In this section, we sketch the history of each of these aspects. compiler implementation.
Second, extensible compilers are not libraries in the same
8.1 Extensible Static Semantics sense as ordinary libraries. Instead, they are plug-ins to a different
application—the compiler, which may be written in a different lan-
Macros have a long history in Lisp and Scheme (Steele Jr. and
guage using a different architecture. This means compiler plugins
Gabriel 1993; Dybvig et al. 1992; Kohlbecker 1986). Several as-
cannot take advantage of the same library distribution mechanisms,
pects of Racket’s macro system improve on prior techniques.
debugging mechanisms, tools, and the rest of the software infras-
First, the #%module-begin macro offers control over the en-
tructure around the language. This difference makes them both
tire module. Many Lisp programmers have approximated it with
more difficult to develop and to use for programmers, thus limiting
explicit wrappers. Second, many Lisp systems provide analogues
language experimentation.
of local-expand under the names expand or macroex-
pand. These features are more limited, however. Most importantly, 8.3 Rewriting-based Optimization
they do not compose with other macros (Culpepper and Felleisen
2010). Third, Chez Scheme (Dybvig 2009) provides a define- The specific optimization techniques described in section 7 are not
property form, which simulates identifier-keyed tables for the new to Typed Racket. Kelsey (1989) presents a transformational
same purposes described in section 5. Chez Scheme’s form is less compiler that expresses optimizations as rewritings from the source
general than Racket’s: while define-property would support language, an extended continuation-passing style lambda calculus,
Typed Racket’s maintenance of type environments, it would not to itself. These transformations are encoded inside the compiler,
work for the interoperability mechanism described in section 6.2. however, which places them off-limits for programmers.
Fisher and Shivers (2006; 2008) present a system for handling The Glasgow Haskell Compiler (Peyton Jones 1996) also ex-
analysis of programs written in an extensible language. Specifi- presses optimizations as rewrite rules. Unlike Kelsey’s work, GHC
cally, their system can implement static analyses, such as type sys- makes it possible for programmers to supplement these rules with
tems, on top of extensible languages, without requiring the anal- their own. To preserve safety, the compiler enforces that all rewrite
ysis to know about every possible language extension. The sys- rules must preserve types; that is the result of applying a rewrite
tem employs a dispatch mechanism so that each extension can an- rule must be of the same type as the the original term. In contrast to
swer the static analysis question about its own use. This approach Racket, the GHC rewrite rules are expressed in a limited domain-
is extensible and expressive, but inherently unsound, because lan- specific language.
guage extensions may provide analysis results that disagree with It would be impossible to implement the arity-raising transfor-
their runtime behavior. Our solution using local-expand relies mation mentioned in section 7.2 in the GHC framework; such opti-
on the inference of high-level properties from core forms and re- mizations are instead built-in to GHC.
spects soundness, though it imposes limits on some macro uses in
language extensions. 9. Conclusion
In the functional-logic programming world, the Ciao Prolog In Growing a Language, Steele writes “if we grow the language in
system (Hermenegildo et al. 2008) comes close to Racket. It also al- these few ways, then we will not need to grow it in a hundred other
lows programmers to annotate their programs with new static asser- ways; the users can take on the rest of the task.” In this paper, we
tions and to control when these assertions are checked. This strat- have explained how Racket’s small number of language extensibil-
egy accommodates customizable static semantics and enabling new ity mechanisms empower the application programmer to grow even
optimizations. In contrast to our system, Ciao builds the assertion a highly sophisticated language. While these mechanisms continue
language into the compiler, which furthers tight integration with the long tradition of Lisp and Scheme macros, they also go far
its existing static analyzers and compiler, but prevents users from beyond macros. Most importantly, Racket programmers can use
developing truly new semantics and whole program checking. #%module-begin to create libraries that process modules in a
The ArBB and Rapidmind (Ghuloum et al. 2010) tools take context-sensitive manner—without interfering with macros. Fur-
the approach of embedding languages for novel execution models ther, the syntax system can use attributed ASTs to communicate
within C++. In contrast with the approach we present, their embed- out-of-band information. As a result, programmers can interpolate
ding demands the use of the C++ type system and necessitates the context-sensitive analyses and source-based optimizations where
creation of ad-hoc replacements for fundamental language features the latter may exploit the result of the former.
such as ‘if’ and ‘for’ statements, thus limiting linguistic reuse. Ad- One running example, Typed Racket, illustrates the range of
ditionally, because individual constructs see only a portion of the Racket’s extensibility mechanism. Specifically, the implementation
program, the scope for an analysis is necessarily local, in contrast of Typed Racket covers almost all aspects of language experimenta-
to Typed Racket which can check an entire module. tion, from the front end to the back end. As our benchmarks demon-
8.2 Extensible Compilers strate, the current backend of Typed Racket delivers competitive
performance. What reduces the performance from “highly compet-
Since compilers are valuable and complex pieces of software, many itive” to just “competitive” is the lack of Racket APIs for process-
projects have designed extensible compilers. Extensibility means ing intermediate representations and/or JIT compiler information.
that programmers can add front-ends, backends, optimizations, Until such APIs are available, compiler optimizations for language
analyses, or other plug-ins (Bravenboer and Visser 2004; Bachrach extensions remain limited to source-level transformations.
and Playford 2001; Cox et al. 2008; Nystrom et al. 2003). All of
these systems differ significantly from our implementation of a
language as a library. First, they require interoperation at the level Acknowledgments
of the compiler rather than the language. Language extensions in Robby Findler and Shriram Krishnamurthi have contributed to this
Racket are implemented as Racket programs, operate on Racket design over many years. Numerous members of the Northeastern
programs, and produce Racket programs; they operate without any PRL provided feedback on earlier drafts of this paper. Mitch Wand
reliance on the details of the underlying compiler or runtime. This suggested the title.
Bibliography Pieter H. Hartel, Marc Feeley, Martin Alt, Lennart Augustsson, Peter Bau-
mann, Marcel Beemster, Emmanuel Chailloux, Christine H. Flood,
Jonathan Bachrach and Keith Playford. The Java syntactic extender. In
Proc. Conf. Object-Oriented Programming Systems, Languages, and Wolfgang Grieskamp, John H. G. van Groningen, Kevin Hammond,
Applications, pp. 31–42, 2001. Bogumi Hausman, Melody Y. Ivory, Richard E. Jones, Jasper Kam-
perman, Peter Lee, Xavier Leroy, Rafael D. Lins, Sandra Loose-
Eli Barzilay and John Clements. Laziness Without All the Hard Work. In more, Niklas Röjemo, Manuel Serrano, Jean-Pierre Talpin, Jon Thack-
Proc. Works. Functional and Declarative Programming in Education, ray, Stephen Thomas, Pum Walters, Pierre Weis, and E.P. Wentworth.
pp. 9–13, 2005. Benchmarking implementations of functional languages with “pseudo-
Martin Bravenboer and Eelco Visser. Concrete syntax for objects: domain- knot” a float-intensive benchmark. J. of Functional Programming 6(4),
specific language embedding and assimilation without restrictions. In pp. 621–655, 1996.
Proc. Conf. Object-Oriented Programming Systems, Languages, and Manuel V. Hermenegildo, Francisco Bueno, Manuel Carro, Pedro Lopez,
Applications, pp. 365–383, 2004. José F. Morales, and German Puebla. An overview of the Ciao multi-
John Clements, Matthias Felleisen, Robert Bruce Findler, Matthew Flatt, paradigm language and program development environment and its de-
and Shriram Krishnamurthi. Fostering Little Languages. Dr. Dobb’s sign philosophy. In Concurrency, Graphs and Models. Springer-Verlag,
Journal, pp. 16–24, 2004. pp. 209–237, 2008.
William D. Clinger and Lars Thomas Hansen. Lambda, the ultimate label Richard Andrew Kelsey. Compilation by Program Transformation. PhD
or a simple optimizing compiler for Scheme. In Proc. Conf. on LISP dissertation, Yale University, 1989.
and functional programming, pp. 128–139, 1994. Eugene Kohlbecker. Syntactic Extensions in the Programming Language
Russ Cox, Tom Bergan, Austin T. Clements, Frans Kaashoek, and Eddie Lisp. PhD dissertation, Indiana University, 1986.
Kohler. Xoc, an extension-oriented compiler for systems programming. Shriram Krishnamurthi. Linguistic Reuse. PhD dissertation, Rice Univer-
In Proc. Conf. Architectural Support for Programming Languages and sity, 2001.
Operating Systems, pp. 244–254, 2008.
Shriram Krishnamurthi, Peter Walton Hopkins, Jay McCarthy, Paul T.
Ryan Culpepper and Matthias Felleisen. Debugging hygienic macros. Sci- Graunke, Greg Pettyjohn, and Matthias Felleisen. Implementation and
ence of Computer Programming 75(7), pp. 496–515, 2010. use of the PLT Scheme web server. Higher-Order and Symbolic Com-
Ryan Culpepper and Matthias Felleisen. Fortifying macros. In Proc. Inter- puting 20(4), pp. 431–460, 2007.
national Conf. on Functional Programming, pp. 235–246, 2010. Simon Marlow. Haskell 2010 Language Report. 2010.
Ryan Culpepper, Scott Owens, and Matthew Flatt. Syntactic abstraction Robin Milner, Mads Tofte, Robert Harper, and David MacQueen. The
in component interfaces. In Proc. Conf. Generative Programming and Definition of Standard ML, Revised Edition. MIT Press, 1997.
Component Engineering, pp. 373–388, 2005.
Nathaniel Nystrom, Michael Clarkson, and Andrew Myers. Polyglot: an
Ryan Culpepper, Sam Tobin-Hochstadt, and Matthias Felleisen. Advanced extensible compiler framework for Java. In Proc. International Conf.
Macrology and the Implementation of Typed Scheme. In Proc. Scheme on Compiler Construction, pp. 138–152, 2003.
and Functional Programming, 2007.
Simon L Peyton Jones. Compiling Haskell by program transformation a
R. Kent Dybvig. Chez Scheme Version 8 User’s Guide. Cadence Research report from the trenches. In Proc. European Symp. on Programming,
Systems, 2009. pp. 18–44, 1996.
R. Kent Dybvig, Robert Hieb, and Carl Bruggeman. Syntactic abstraction Hari Prashanth K R and Sam Tobin-Hochstadt. Functional data structures
in Scheme. Lisp and Symbolic Computation 5(4), pp. 295–326, 1992. for Typed Racket. In Proc. Works. Scheme and Functional Program-
Marc Feeley and James S. Miller. A parallel virtual machine for efficient ming, pp. 1–7, 2010.
Scheme compilation. In Proc. Conf. on LISP and Functional Program- Manuel Serrano and Pierre Weis. Bigloo: a portable and optimizing com-
ming, pp. 119–130, 1990. piler for strict functional languages. In Proc. Static Analysis Symp., pp.
Matthias Felleisen, Robert Bruce Findler, Matthew Flatt, and Shriram Kr- 366–381, 1995.
ishnamurthi. Building Little Languages With Macros. Dr. Dobb’s Jour- Michael Sperber, Matthew Flatt, Anton Van Straaten, R. Kent Dybvig,
nal, pp. 45–49, 2004. Robert Bruce Findler, and Jacob Matthews. Revised6 report on the
David Fisher and Olin Shivers. Static semantics for syntax objects. In Proc. algorithmic language Scheme. J. of Functional Programming 19(S1),
International Conf. on Functional Programming, pp. 111–121, 2006. pp. 1–301, 2009.
David Fisher and Olin Shivers. Building language towers with Ziggurat. J. Guy L. Steele Jr. Common Lisp: The Language. Second edition. Digital
of Functional Programming 18(5-6), pp. 707–780, 2008. Press, 1994.
Matthew Flatt. Composable and compilable macros: you want it when? Guy L. Steele Jr. Growing a language, Keynote at OOPSLA 1998. Higher-
In Proc. International Conf. on Functional Programming, pp. 72–83, Order and Symbolic Computation 12(3), pp. 221–236, 1999.
2002. Guy L. Steele Jr. and Richard P. Gabriel. The evolution of Lisp. In Proc.
Matthew Flatt, Eli Barzilay, and Robert Bruce Findler. Scribble: closing the Conf. on History of Programming Languages, pp. 231–270, 1993.
book on ad-hoc documentation tools. In Proc. International Conf. on
T. Stephen Strickland, Sam Tobin-Hochstadt, and Matthias Felleisen. Prac-
Functional Programming, pp. 109–120, 2009.
tical Variable-Arity Polymorphism. In Proc. European Symp. on Pro-
Matthew Flatt and Matthias Felleisen. Units: Cool modules for HOT lan- gramming, 2009.
guages. In Proc. Conf. on Programming Language Design and Imple-
Sam Tobin-Hochstadt and Matthias Felleisen. Interlanguage refactoring:
mentation, pp. 236–248, 1998.
from scripts to programs. In Proc. Dynamic Languages Symp., pp. 964–
Matthew Flatt, Robert Bruce Findler, and Matthias Felleisen. Scheme with 974, 2006.
classes, mixins, and traits. In Proc. Asian Symp. on Programming Lan- Sam Tobin-Hochstadt and Matthias Felleisen. The design and implemen-
guages and Systems, pp. 270–289, 2006. tation of Typed Scheme. In Proc. Symp. on Principles of Programming
Matthew Flatt and PLT. Reference: Racket. PLT Inc., PLT-TR-2010-1, Languages, pp. 395–406, 2008.
2010. http://racket-lang.org/tr1/ Sam Tobin-Hochstadt and Matthias Felleisen. Logical types for untyped
Richard P. Gabriel. Performance and Evaluation of LISP Systems. MIT languages. In Proc. International Conf. on Functional Programming,
Press, 1985. pp. 117–128, 2010.
Anwar Ghuloum, Amanda Sharp, Noah Clemons, Stefanus Du Toit, Rama
Malladi, Mukesh Gangadhar, Michael McCool, and Hans Pabst. Array
Building Blocks: A Flexible Parallel Programming Model for Multicore
and Many-Core Architectures. Dr. Dobb’s Journal, 2010.

You might also like