Documents
Documents
(cond This idiom, seen here in member, uses arbitrary non-#f values as
[(and (number? (node-left x)) (string? (node-right y))) true and uses #f as a marker for missing results, analogous to
;; known: (node-right y) is a string, (node-left x) is a number ML’s NONE. The type for member specifies this behavior with an
...] appropriate type signature. It can thus infer that in the then branch,
[(number? (node-left x)) x has the type of the desired result and is not #f.
;; known: (node-right y) is not a string
2.2 Challenges
...]
[(string? (node-right y)) Of course, programmers write tests beyond simple applications of
;; known: (node-left x) is not a number predicates such as (number? x). For example, logical connectives
. . . ]) can combine the results of predicates:2
Now the programmer and the revised type system both determine . . . (if (or (number? x) (string? x)) (f x) 0) . . . Example 4
that since (number? (node-left x)) is true in the second clause,
(string? (node-right y)) must be false, and thus, (node-right y) is For this fragment to typecheck, the type system must recognize
S
not a string. Using propositional logic to reason about predicates that (or (number? x) (string? x)) ensures that x has type ( String
handles this and many other similar situations. Number) in the then branch, the domain of f from example 2.
Beyond the new type system, this paper contributes: For and, there is no such neat connection:
• a full-fledged implementation of the calculus, now known as . . . (if (and (number? x) (string? y)) Example 5
Typed Racket, addressing the full complexities of the functional (+ x (string-length y))
core of Racket, such as mutable data and multiple arguments; 0) . . .
• an empirical study of the usefulness of our extensions; and Example 5 is perfectly safe, regardless of the values of x and y.
• a novel model-theoretic proof technique for type soundness. In contrast, the next example shows how little we know when a
conjunction evaluates to false:
The paper begins with a brief review of the essence of occur-
rence typing with an emphasis on programming idioms that our ;; x is either a Number or a String Example 6
original system cannot typecheck. We then describe the new sys- . . . (if (and (number? x) (string? y))
tem in a semi-formal manner. In the three following sections, we (+ x (string-length y))
describe the system formally: first the core system of occurrence (string-length x)) . . .
typing, then several extensions demonstrating the expressiveness
Here a programmer falsely assumes x to be a String when the test
of the system, and third the proof of soundness. These sections
fails. But, the test may produce #f because x is actually a String,
are followed by a description of our implementation strategy and
or because y is not a String while x is a Number. In the latter case,
empirical measures of its usefulness on existing code. Finally, we
(string-length x) fails. In general, when a conjunction is false, we
discuss related work and conclude.
do not know which conjunct is false.
Finally, and is expressible using nested if expressions, a pattern
2. A Brief Introduction to Occurrence Typing that is often macro-generated:
Here is the simplest example of occurrence typing: . . . (if (if (number? x) (string? y) #f) Example 7
(+ x (string-length y))
. . . (if (number? x) (add1 x) 0) . . . Example 1
0) . . .
Regardless of the value of x, this program fragment always pro- One way for the type system to deal with this pattern is to reason
duces a number. Thus, our type system should accept this fragment, that it is equivalent to the conjunction of the two predicates.
regardless of the type assigned to x, even if the type is not legitimate So far, we have seen how programmers can use predefined
for add1. The key to typing this program is to assign the second oc- predicates. It is important, however, that programmers can also
currence of x a different, more precise type than it has in the outer abstract over existing predicates:
context. Fortunately, we know that for any value of type Number,
number? returns #t; otherwise, it returns #f. Therefore, it is safe (define: (strnum? [x : >]) ;; > is the top type Example 8
to use Number as the type of x in the then branch. (or (string? x) (number? x)))
S
2.1 Existing Capabilities Take the previous example of a test for ( String Number). A
programmer can use the test to create the function strnum?, which
The following function f always produces a number: behaves as a predicate for that type. This means the type system
S
(define: (f [x : ( String Number)]) Example 2
must represent the fact that strnum? is a predicate for this type, so
(if (number? x) (add1 x) (string-length x))) that it can be exploited for conditionals.
If (number? x) produces #t, x is an appropriate input for add1. 2 Theoriginal system could handle only an encoding of or, with different
If it produces #f, x must be a String by process of elimination; semantics than that provided by Racket.
In example 4, we saw the use of or to test for disjunctions. Like 2.3 Putting it all Together
and, or is directly expressible using if: Our type system correctly handles all of the preceding examples.
Finally, we combine these features into an example that demon-
(if (let ([tmp (number? x)]) Example 9
strates all aspects of our system:
(if tmp tmp (string? x)))
(f x)
S
(λ: ([input : ( Number String)] Example 14
0) [extra : h>, >i])
(cond
The expansion is analyzed as follows: if (number? x) is #t, then [(and (number? input) (number? (car extra)))
so is tmp, and thus the result of the inner if is also #t. Otherwise, (+ input (car extra))]
the result of the inner if is (string? x). This code presents a new [(number? (car extra))
challenge for the type system, however. Since the expression tested (+ (string-length input) (car extra))]
in the inner if is the variable reference tmp, but the system must [else 0]))
also learn about (number? x) from the test of tmp.
In section 5.3, we return to this example with a type system that
Selectors All of the tests thus far only involve variables. It is also checks it correctly.
useful to subject the result of arbitrary expressions to type tests:
3. How to Check the Examples
Example 10
. . . (if (number? (car p)) (add1 (car p)) 7) . . . Next we use the preceding examples to explain the basic ideas of
our new system for occurrence typing.
Even if p has the pair type h>, >i, then example 10 should pro-
duce a number.3 Of course, simply accommodating repeated appli- 3.1 Propositions and Objects
cations of car is insufficient for real programs. Instead, the rele-
Recall example 1:
vant portions of the type of p must be refined in the then and else
branches of the if. (if (number? x) (add1 x) 0)
In the next example:
In this example, the typechecker must propagate information from
(λ: ([p : h>, >i]) Example 11 the test to the then branch. Therefore, the typechecker really proves
(if (and (number? (car p)) (number? (cdr p))) the proposition that “if the test evaluates to #t, then x is a number”,
(g p) a proposition abbreviated as Nx , with N short for Number. The
’no)) typechecker then uses this proposition to check the then branch.
The proposition Nx is computed from (number? x) by combin-
the test expression refines the type of p from the declared h>, >i ing information from two sources. On one hand, the type of num-
to the required hNumber, Numberi. This is the expected result of ber? includes the information that it is a predicate. On the other,
the conjunction of tests on the car and cdr fields. testing x produces information about the variable x.
Example 12 shows how programmers can simultaneously ab- The addition of a proposition as part of the type of the number?
stract over the use of both predicates and selectors: function accomplishes the first goal. Specifically, the added propo-
sition allows the type of a function to describe what propositions
(define carnum? Example 12 are derivable when the function produces a true value. Borrowing
(λ: ([x : h>, >i]) (number? (car x)))) terminology from work on effect systems [Lucassen and Gifford
1988], we refer to these propositions as latent. Borrowing notation
The carnum? predicate tests if the car of its argument is a Number, from dependent types, we name the argument in each function, al-
and its type must capture this fact. lowing latent propositions to be well-scoped in function types. If
the argument to number? is named y, the latent proposition is Ny .
Reasoning Logically Of course, we do learn something when This makes the type of number?:
conjunctions such as those in examples 5 and 6 are false. When Ny
a conjunction is false, we know that one of the conjuncts is false, y:>−−→B
and thus when all but one are true, the remaining one must be
false. This reasoning principle is used in multi-way conditionals, To satisfy the second goal, we modify the type system so that it
which is a common idiom extensively illustrated in How to Design derives an object for each expression. The object describes which
Programs [Felleisen et al. 2001]: part of the environment an expression accesses. In our example, it
is simply x.
. . . (cond Example 13 Given these pieces of information, the typechecker obtains the
[(and (number? x) (string? y)) — 1 —] desired proposition about a predicate application from the substi-
[(number? x) — 2 —] tution of the actual object for the formal parameter in the latent
[else — 3 —]) . . . proposition. For the first example, the result isSNx .
In example 2, x initially has the type ( String Number).
To check the else branch, the typechecker needs the information
This program represents a common idiom. In clause 1, we obvi-
that x is not a Number; i.e., that it is a String. It computes this
ously know that x is a Number and y is a String. In clause 2, x is
information via two propositions, one for each of the then and else
again a Number. But we also know that y cannot be a String. To
branches. For the then branch the proposition is Nx , as above. For
effectively typecheck such programs, the type system must be able
the else branch, the type checker must propagate the proposition “x
to follow this reasoning.
is not a Number”—written Nx —from the test to the else branch.
To this end, function types are actually equipped with two latent
3 Racket pairs are immutable; this reasoning is unsound for mutable pairs. propositions: one for when the function produces a true value, and
one for when it produces a false value. Thus, the type of number? 3.5 Selectors
is now The essence of example 10 is the application of predicates to se-
Ny |Ny lector expressions, e.g., (car p). Our type system represents such
y:>−−−−−→B
expressions as complex objects. For example, (number? (car p))
with the two propositions separated by |. Substituting x for y in the involves a predicate with latent propositions Nx |Nx applied to an
latent propositions produces the desired results. expression whose object indicates that it accesses the car field of
Contrary to appearances, pairs of propositions need not be com- p. We write car(p) for this object. Thus, the entire expression has
plementary. Recall (and (number? x) (> x 100)) from section 1. In proposition Ncar(p) for the then branch and Ncar(p) for the else
this case, the then proposition should be Nx , because if the and ex- branch, obtained by substituting car(p) for x in the latent propo-
pression produces #t, x must be a number. But the else proposition sitions. Combinations of such tests (example 11) and abstraction
cannot be Nx , since x might have been 97, which would produce over them (example 12) work as before.
#f but is nonetheless a number. To specify the access behavior of selectors, each function type is
equipped with a latent object, added below the arrow. For car, it is
3.2 Handling Complex Tests
car. But, since selectors can be composed arbitrarily, the function
For complex tests, such as those of example 4, the type system com-
bines the propositions of different subexpressions. In the cited ex- (λ: ([x : h>, h>, >ii]) (car (cdr x)))
ample, the propositions for (number? x) and (string? x) are Nx |Nx has the type
and Sx |Sx , respectively. For the or expression, these should be
combined to Nx ∨ Sx for the then branch and Nx ∧ Sx for the #fcar(cdr(x )) |#fcar(cdr(x ))
x:h>, h>, >ii−−−−−−−−−−−−−−−−−→>
else branch. car(cdr(x))
From these complex propositions, the typechecker derives
propositions about the type of x. If x is a number or a string, x 3.6 Reasoning Logically
S
is in ( N S). By S codifying this as a rule of inference, it is pos- Next we revisit conjunctions such as (and (number? x) (string? y)).
sible to derive ( N S)x from Nx ∨ Sx , just what is needed to If this expression evaluates to #f, the typechecker can infer some
S then branch. From Nx ∧ Sx it is similarly possible to
check the propositions about x and y. In particular, since the original expres-
derive ( N S)x , as expected for the else branch. To propagate sion derived the proposition Nx ∨ Sy for the else branch, the type
propositions, we use a proposition environment instead of a type system can combine this environmental information with the re-
environment; the type environment becomes a special case. sults of subsequent tests. In example 13, the type system derives
Examples 5 and 6 are dealt with in the same manner, but with the proposition Nx when the second cond clause produces true,
conjunction instead of disjunction. In example 7, the test expression which means Sy holds, too. In short, maintaining propositions in
of the outer if is itself an if expression. The typechecker must the environment allows the typechecker to simulate the reasoning
derive propositions from it and propagate them to the then and of the programmer and to track the many facts available for deduc-
else branches. Thus, it first computes the propositions derived for ing type correctness for an expression.
each of the three subexpressions, giving Nx |Nx for the test and
3.7 The Form of the Type System
Sy |Sy for the then branch. Since the else branch—a plain #f—
never produces a true value, the relevant propositions are ff and The essence of our discussion can be distilled into five ideas:
tt, the impossible and trivial propositions.
• Propositions express relationships between variables and types.
3.3 Abstracting over Predicates • Instead of type environments, we use proposition environments.
The next challenge, due to example 8, is to include proposition • Typechecking an expression computes two propositions, which
information in function types for user-defined predicates: hold when the expression evaluates to true or false, respectively.
(λ: ([x : >]) (or (string? x) (number? x))) • Typechecking an expression also determines an object of in-
As explained above, the typechecker quiry, describing the particular piece of the environment pointed
S Sassigns the body of the func- to by that expression. This piece of the environment may also
tion type B and derives ( N S)x |( N S)x as the then and else
propositions. To add these to a function type, it merely moves these be a portion of a larger data structure, accessed via a path.
propositions into the arrow type: • Latent propositions and objects are attached to function types in
order to describe facts about the result of applying the function.
( N S)x |(S N
S)x
S
x:>−−−−−−−−−−−−−→B The next sections translate these ideas into a typed calculus, λTR .
The key to the simplicity of this rule is that the bound variable of
the λ expression becomes the name of the argument in the function 4. The Base Calculus
type, keeping the propositions well-scoped. We begin our presentation of λTR with the base system, a typed
lambda calculus with booleans, numbers, and conditionals. In Sec-
3.4 Variables as Tests
tion 5, we extend the system with pairs and local variable binding.
In examples 3 and 9, the test expression is just a variable. For such The fundamental judgment of the type system is
cases, the typechecker uses the proposition #fx in the else branch
to indicate that variable x has the value #f. Conversely, in the then Γ ` e : τ ; ψ+ |ψ− ; o
branch, the variable must be true, giving the proposition #fx . It states that in environment Γ, the expression e has type τ, comes
Example 9 demands an additional step. In the then branch of with then proposition ψ+ and else proposition ψ− , and references
the if, tmp must be true. But this implies that (number? x) must also object o. That is, if e evaluates to a true value, then proposition
be true; the propsition representing this implication, #ftmp ⊃ Nx , ψ+ holds; conversely, if e evaluates to a false value, ψ− is true.
is added to the environment used to check the body of the let Further, if e evaluates to a value, then looking up o in the runtime
expression. environment produces the same value.
d, e ::= x | (e e) | λx τ.e | (if e e e) | c | #t | #f | n Expressions
c ::= add1 | zero? | number ? | boolean? | procedure? Primitive Operations
σ, τ ::= > | N | #t | #f | ( −
S → ψ|ψ
τ ) | x:τ −−→τ Types
o
ψ ::= τx | τ x | ψ ⊃ ψ | ψ ∨ ψ | ψ ∧ ψ | ff | tt Propositions
o ::= x | ∅ Objects
−
→
Γ ::= ψ Environments
4.1 Syntax for a variable indicates that if x evaluates to a true value, x cannot
The syntax of expressions, types, propositions, and objects is given have type #f. Similarly, if x evaluates to #f, its type is #f.
in figure 1. The expression syntax is standard, with conditionals, Abstraction and Application The rule for checking an abstrac-
numeric and boolean constants, and primitive operators, in addition tion, T-A BS, takes the propositions and object from the body and
to the basics of abstraction, application, and variable reference. makes them the latent propositions and object in the function type.
Abstractions come with type annotations on the bound variable. By taking the bound variable from the abstraction and turns it into
The presentation of the standard operational semantics is deferred the name of the argument, references to the variable in the types,
to section 6, in conjunction with the soundness proof. propositions, and object remain well-scoped.
As for types, > is the supertype of all types; N is the type Correspondingly, in T-A PP, the latent propositions and object
of numeric values; #t and #fSare the types of the true and false are used as the result propositions and object, just as with the result
constants, respectively; and ( − →τ ) isSthe untagged or “true”S
union type. In all cases, the actual object oa is substituted for the name of
of its components. We abbreviate ( #t #f) as B and ( ) as the formal parameter, x. Consider the abstraction:
⊥. Function types name their arguments. This name is in scope
in the latent propositions and objects of a function type—written λy >.(number ? y)
above and below the arrow, respectively—and in the result type.
The latent propositions are knowledge about the types of variables which typechecks as follows. In the body of the abstraction, Γ =
when the function produces a true or false value, respectively. >y . Thus, Γ ` y : > ; #fy |#fy ; y, and number ? has the
Most propositions come in familiar form, borrowed from above-mentioned type. To check the application, the typechecker
propositional logic: implications, disjunctions, and conjunctions, substitutes y for x in the result type, latent propositions, and latent
plus always-true (tt) and always-false (ff ) propositions. Atomic object of number ?, which yields Γ ` (number ? y):B ; Ny |Ny ; ∅.
propositions relate variables to their types: τx states that x has type Finally, the function is assigned the desired type via T-A BS:
τ; τ x states that x never assumes a value with type τ.
Ny |Ny
An object describes a portion of the runtime environment. In the y:>−−−−−→B
∅
base system, it is either a variable or the empty object. For example,
the expression (add1 7) has object ∅ because it does not access any In our prior system, this example required multiple special-
portion of the environment. purpose rules and the use of several metafunctions, whereas here
Environments are simply collections of arbitrary propositions. it is a simple matter of scope and substitution.
Of course, substitution of an object for a variable must account
Types and Propositions Unlike many systems that relate type for the case when the object is ∅. When this happens, any references
systems and logic, λTR distinguishes types, propositions, and to the variable are forgotten, and propositions or objects that refer
terms. Propositions state claims about the runtime environment to it become trivial. Figure 8 defines the full substitution function.
and thus relate types and variables. This choice allows a simple
and decidable logic to be used to derive types from propositions to Conditionals As far as types and objects are concerned, the T-I F
achieve the desired expressiveness. rule is straightforward. The test may have any type and object, and
the then and else branches must have identical types and objects,
4.2 Typing Rules which then become the type and the object of the entire expression.
Figures 2 and 3 specify the typing and subtyping rules. The key difference between T-I F and conventional rules for condi-
tionals is due to the differential propagation of knowledge from the
Constants The simplest rule is T-N UM, which gives all numbers test to the branches. Specifically, the rule uses two distinct environ-
the type N. Since numbers are treated as true by the evaluation ments to check the then and else branches, because ψ1+ holds in
rules for if, numeric constants are assigned the propositions tt|ff , the then branch and ψ1− holds in the else branch.
indicating that no new information is acquired when the number The resulting propositions follow from a simple principle about
evaluates to a true value; if it evaluates to false, a contradiction the evaluation of if. If a true value is produced, either the then
is obtained. The rule for function constants, T-C ONST, work in branch or the else branch must have evaluated to a true value, and
similar manner, though we use a δτ function to assign types to similarly for a false value. Therefore, in the true case, either the
function constants. The boolean constants are given singleton types then proposition of the then branch, ψ2+ , or the then proposition
by T-T RUE and T-FALSE, along with propositions that reflect that of the else branch, ψ3+ , must be true, which means ψ2+ ∨ ψ3+ is
#t is always true, and #f is always false. All of the constants have the then proposition of the entire expression and, correspondingly,
the object ∅, since none refer to any portion of the environment. ψ2− ∨ ψ3− is the else proposition.
Variables The rule for typing variables, T-VAR, exploits the proof Subsumption & Subtyping Finally, λTR comes with subtyping.
system. If the current environment proves that x has type τ, repre- Expressions of type τ can be viewed as having a larger type τ 0 .
sented by the proposition τx , then the type system assigns x the Objects can also be lifted to larger objects. The ordering on propo-
type τ. The object for a variable is itself. Finally, the propositions sitions is simply provability in the current environment.
T-N UM T-C ONST T-T RUE T-FALSE
Γ ` n : N ; tt|ff ; ∅ Γ ` c : δτ (c) ; tt|ff ; ∅ Γ ` #t : #t ; tt|ff ; ∅ Γ ` #f : #f ; ff |tt ; ∅
T-A PP
ψf |ψf
+ −
Γ ` e : x:σ −−−−−−→τ ; ψ+ |ψ− ; o
T-VAR T-A BS of
Γ ` τx Γ, σx ` e : τ ; ψ+ |ψ− ; o Γ ` e 0 : σ ; ψ+ 0 |ψ− 0 ; o 0
Γ ` x : τ ; #fx |#fx ; x ψ+ |ψ−
Γ ` λx σ.e : x:σ −−−−→τ ; tt|ff ; ∅ Γ ` (e e ) : τ[o 0 /x] ; ψf+ |ψf− [o 0 /x] ; of [o 0 /x]
0
S-F UN
SO-R EFL S-R EFL S-U NION S UPER S-U NION S UB
−−−−−−→i ` σ 0 <: σ ` τ <: τ 0
` o <: o ` τ <: τ ∃i. ` τ <: σi ` τi <: σ ψ+ ` ψ + 0
ψ− ` ψ− 0 ` o <: o 0
−
→ −
→ ψ+ 0 |ψ− 0
[ [
SO-T OP S-T OP ` τ <: ( σ i) `( τ i) <: σ ψ+ |ψ−
` x:σ −−−−→τ <: x:σ 0 − →τ 0
−−−0−−
` o <: ∅ ` τ <: > o o
Given these definitions, the rules for subtyping are straightfor- tion Nx ∧ Sy |tt since ff ` Nx ∧ Sy . Therefore, the entire inner if
ward. All types are subtypes of > and of themselves. Subtypes of expression has then proposition
elements of a union are subtypes of the union, and any type that is
a supertype of every element is a supertype of the union. Finally, (Nx ∧ Sy ) ∨ (Nx ∧ Sy ) = Nx ∧ Sy
function types are ordered in the usual fashion. and else proposition tt.
Second, we typecheck the then branch of the main if expression
4.3 Proof System in the environment Γ1 = >x , >y , Nx ∧ Sy . Since Γ1 ` Nx and
Figure 4 specifies the proof rules for our logic. The first nine Γ1 ` Sy , we can give x and y the appropriate types to check the
rules—L-ATOM through L-O R E—use the natural deduction style expression (+ x (string-length y)).
to express the standard rules of propositional logic.
The subsequent four rules relate the atomic propositions. In
particular, L-S UB says that if x has type τ, then it has any larger 5. Extensions
type. Similarly, L-S UB N OT says that if x does not have type τ, then The base system of section 4 lacks several important features, in-
it does not have any smaller type. By L-B OT, if x has an empty cluding support for compound data structures and let. This section
type, it is possible to conclude anything since this is impossible. shows how to extend the base system with these features.
The L-U PDATE rule refines the type of a variable via a combina-
tion of multiple propositions. Roughly speaking, this metafunction 5.1 Pairs
satisfies the equations The most significant extension concerns compound data, e.g., pairs.
update(τ, σ) = τ ∩ σ update(τ, σ) = τ − σ We extend the expression, type, and proposition grammars as
shown in figure 5.4 Most significantly, in all places where a variable
See figure 9 for the full definition. appeared previously in propositions and objects, it is now legal to
4.4 A Worked Example specify a path—a sequence of selectors—rooted at a variable, writ-
ten π(x). This allows the system to refer not just to variables in the
At this point, eight of our 14 examples typecheck. To illustrate the environment, but to parts of their values.
workings of the type system, let us work example 7:
Typing Rules Figure 6 shows the extensions to the typing and
(if (if (number? x) (string? y) #f)
subtyping rules. Again, the subtyping rule S-PAIR and typing rule
(+ x (string-length y))
for cons are straightforward; all pair values are treated as true.
0)
The T-C AR and T-C DR rules are versions of the application rule
First, assume that the initial environment is Γ = >x , >y . Now specialized to the appropriate latent propositions and objects, which
consider the inner if expression. The test has then proposition Nx here involve non-trivial paths. Substitution of objects for variables
and else proposition Nx . The then branch has propositions Sy is also appropriately extended; the full definition is in figure 8.
and Sy , or by subsumption Nx ∧ Sy |tt, since T-I F adds the then None of the existing typing rules require changes.
proposition of the test to the environment for checking the then
branch. The else branch has propositions ff |tt, and by subsump- 4 In a polymorphic λTR , pair operations could be added as primitives.
L-A ND I
L-ATOM L-FALSE Γ ` ψ1 L-A ND E
ψ∈Γ L-T RUE Γ ` ff Γ ` ψ2 Γ, ψ1 ` ψ or Γ, ψ2 ` ψ
Γ ` tt
Γ`ψ Γ`ψ Γ ` ψ1 ∧ ψ2 Γ, ψ1 ∧ ψ2 ` ψ
L-I MP E L-O R E
L-I MP I Γ ` ψ1 L-O R I Γ, ψ1 ` ψ
Γ, ψ1 ` ψ2 Γ ` ψ1 ⊃ ψ 2 Γ ` ψ1 or Γ ` ψ2 Γ, ψ2 ` ψ
Γ ` ψ1 ⊃ ψ2 Γ ` ψ2 Γ ` ψ1 ∨ ψ 2 Γ, ψ1 ∨ ψ2 ` ψ
T-C AR T-C DR
S-PAIR T-C ONS Γ ` e : hτ1 , τ2 i ; ψ0+ |ψ0− ; o Γ ` e : hτ1 , τ2 i ; ψ0+ |ψ0− ; o
` τ1 <: τ2 Γ ` e1 : τ1 ; ψ1+ |ψ1− ; o1 ψ+ |ψ− = #fcar(x) |#fcar(x) [o/x] ψ+ |ψ− = #fcdr(x) |#fcdr(x) [o/x]
` σ1 <: σ2 Γ ` e2 : τ2 ; ψ2+ |ψ2− ; o2 or = car(x)[o/x] or = cdr(x)[o/x]
` hτ1 , σ1 i <: hτ2 , σ2 i Γ ` (cons e1 e2 ) : hτ1 , τ2 i ; tt|ff ; ∅ Γ ` (car e) : τ1 ; ψ+ |ψ− ; or Γ ` (cdr e) : τ2 ; ψ+ |ψ− ; or
Logic Rules Figure 7 specifies the changes to the logic for deal- ing us the following rule:
ing with paths. For the first three rules, the only change needed T-L ET
is allowing paths in the appropriate syntactic locations. For the L- Γ ` e0 : τ ; ψ0+ |ψ0− ; o0
U PDATE rule, there is an additional change. When the environment Γ, τx , #fx ⊃ ψ0+ , #fx ⊃ ψ0− ` e1 : σ ; ψ1+ |ψ1− ; o1
proves both h>, >ix and Ncar(x) , it must be possible to derive
hN, >ix . The new version of L-U PDATE allows this inference via Γ ` (let (x e0 ) e1 ) : σ[o0 /x] ; ψ1+ |ψ1− [o0 /x] ; o1 [o0 /x]
a revised version of update. Its third argument specifies a path to This rule has three components. The first antecedent checks the
follow before refining the type. See figure 9 for details. right-hand side. The second checks the body with an environment
Of course, none of the rules implementing the standard proof extended both with the type of the bound variable (τx ) and with
theory of propositional logic change with this extension. implications stating that if x is not false, e0 must evaluate to
With the addition of pairs, the type system can cope with 12 of true, and similarly if x is false, e0 must evaluate to false. The
the 14 examples from section 2. consequence replaces all references to x with the object of e0 .
remove(τ, σ) =⊥ if ` τ <: σ
−−−−−−−−→
remove((∪ −→
τ ), σ) = (∪ remove(τ, σ)) recursion. There are a few tricky cases to consider, however. First,
remove(τ, σ) =τ otherwise if the object being substituted is ∅, then references to the variable
must be erased. In most contexts, such propositions should be
erased to tt, the trivial proposition. But, just as with contravariance
of function types, such propositions must be erased to ff when to
Figure 9. Type Update the left of an odd number of implications. Second, if a proposition
such as τx references a variable z in τ, then if z goes out of scope,
the entire proposition must be erased.
(see example 7), we can therefore derive the then proposition In comparison, the update metafunction is simple. It follows a
Ninput ∧Ncar(extra) and the else proposition Ninput ∨Ncar(extra) . path into its first argument and then appropriately replaces the type
The then proposition, added to the environment for the right-hand there with a type that depends on the second argument. If the sec-
side of the first cond clause, also proves hN, >iextra by the L- ond argument is of the form τ, update computes the intersection
U PDATE rule, which suffices for typechecking the expression (+ of the two types; if the second argument is of the form τ, update
input (car extra)). computes the difference. To compute the intersection and differ-
In the second cond clause, the test has the then proposition ence, update uses the auxiliary metafunctions restrict and remove,
Ncar(extra) , and the environment is Γ0 , Ninput ∨Ncar(extra) From respectively.
this, we can derive Ninput since Ncar(extra) and Ncar(extra) are
S
contradictory. Then, using ( N S)input from Γ0 and Ninput , we
can derive Sinput , which is required to typecheck the application
6. Semantics, Models, and Soundness
of string-length. This completes the second clause. To prove type soundness, we introduce an environment-based oper-
The third clause is a constant and thus obvious. ational semantics, use the environments to construct a model for the
logic, prove the soundness of the logic with respect to this model,
5.4 Metafunctions and conclude the type soundness argument from there.
Equipped with the full formal system, we can now provide a de-
tailed description of the substitution and type update metafunc- 6.1 Operational Semantics
tions; see figures 8 and 9. Figure 11 defines a big-step, environment-based operational se-
Substitution replaces a variable with an object. When the object mantics of λTR . The metavariable ρ ranges over value environ-
is of the form π(x), this is in general a straightforward structural ments (or just environments), which are finite maps from variables
B-D ELTA B-L ET
B-VAR ρ`e⇓c ρ ` e0 ⇓ v ρ ` ea ⇓ va
ρ(x) = v δ(c, v) = v 0 ρ[x 7→ va ] ` eb ⇓ v B-VAL B-A BS
ρ`v⇓v ρ ` λx τ.e ⇓ [ρ, λx τ.e]
ρ`x⇓v ρ ` (e e 0 ) ⇓ v 0 ρ ` (let (x ea ) eb ) ⇓ v
to closed values. We write ρ(x) for the value of x in ρ, and ρ(π(x)) 1. either o = ∅ or ρ(o) = v,
for the value at path π in ρ(x). The central judgment is 2. either v 6= #f and ρ |= ψ+ or v = #f and ρ |= ψ− , and
ρ`e⇓v 3. ` v : τ ; ψ+ 0 |ψ− 0 ; o 0 for some ψ+ 0 , ψ− 0 , and o 0 .
which states that in environment ρ, the expression e evaluates to Proof: By induction on the derivation of the typing judgment.
the value v. Values are given by the following grammar: For illustrative purposes, we examine the T-I F case with e =
(if e1 e2 e3 ). Proving part 1 is trivial: either o is ∅, or both e2 and
v ::= c | #t | #f | n | [ρ, λx τ.e] | (cons v v) e3 have an identical object, and e must evaluate to the results of
For the interpretation of primitives, see figure 13. one of them. To prove part 2, we note that if v = #f, either e2
or e3 must evaluate to #f. If it is e2 , we have ρ |= ψ2− , and thus
6.2 Models ρ |= ψ2− ∨ ψ3− by M-O R, giving the desired result. The cases
for e3 evaluating to false and the whole expression evaluating to
A model is any value environment, and an environment ρ satisfies true are dealt with in an analogous manner. Finally, part 3 is trivial,
a proposition ψ, ρ |= ψ, as defined in figure 12 mostly in the since both the then and else branches have the same type.
usual manner. The satisfaction relation is extended to proposition Given this, we can state the desired soundness theorem.
environments in a pointwise manner. To formulate the satisfaction
relation, we need a typing rule for closures: Theorem 1 (Type Soundness for λTR ). If ` e : τ ; ψ+ |ψ− ; o and
T-C LOS ` e ⇓ v then ` v : τ ; ψ+ 0 |ψ− 0 ; o 0 for some ψ+ 0 , ψ− 0 , and o 0 .
∃Γ.ρ |= Γ and Γ ` λx τ.e : σ ; ψ+ |ψ− ; o Proof: Corollary of lemma 2.
` [ρ, λx τ.e] : σ ; ψ+ |ψ− ; o This theorem comes with the standard drawbacks of big-step
soundness proofs. It says nothing about diverging or stuck terms.5
Two clauses in figure 12—M-T YPE and M-N OT T YPE—need
some explanation. They state that if a value of x in the environment
has the type τ, the model satisfies τx , and if x has a type that does 7. From λTR to Typed Racket
not overlap with τ, the model satisfies τ x . As a core calculus, λTR lacks many of the features of a program-
We can see immediately that not all propositions are consistent, ming language such as Racket, which consists of a rich functional
such as ff , as expected, but also propositions such as Nx ∧ Bx . core enriched with support for object-oriented and component-
Our first lemma says that the proof theory respects models. oriented programming. Creating an implementation from the cal-
Lemma 1. If ρ |= Γ and Γ ` ψ then ρ |= ψ. culus demands additional research and engineering.
This section reports on the most important implementation
Proof: Structural induction on Γ ` ψ. ideas. Our current implementation—dubbed Typed Racket—deals
Conversely, we can use this lemma as the guideline that any with the functional core of Racket, which also supports mutable
logical rule that satisfies this lemma is appropriate.
5 To deal with errors, we would need the following additional steps:
6.3 Soundness for λTR 1. Add an additional value, wrong, which has every type.
With the model theory and the operational semantics in place, we 2. Add evaluation rules that propagate wrong.
can state and prove the second major lemma. 3. Add evaluation rules to generate wrong for each stuck state.
4. Add clauses to the δ function to generate wrong for undefined clauses.
Lemma 2. If Γ ` e : τ ; ψ+ |ψ− ; o, ρ |= Γ, and ρ ` e ⇓ v then 5. Prove that if ` e : τ ; ψ+ |ψ− ; o, then 6` e ⇓ wrong.
all of the following hold:
data structures, assignable variables and Scheme’s multiple value the two propositions. For example, to specify the type of strnum?
returns. In addition, the section discusses user interface issues and in Typed Racket syntax, the user writes
the implementation of the logical reasoning system. S
(: strnum? (> → Boolean : ( String Number)))
S
7.1 Paths and Mutability The syntax states that the latent then proposition is ( S N)x ,
The λTR calculus assumes immutable data structures. In Racket, where x is the name
S of the argument, and the latent else proposition
some forms of data are mutable, however. Predicate tests on paths is conversely ( S N)x , with a latent object of ∅. In practice, this
into mutable data cannot soundly refine the type environment. syntax suffices for capturing the substantial majority of the types
Consider an example using Racket’s equivalent of ref cells: with latent propositions.
Third, Typed Racket uses local type inference and bi-directional
(let∗: ([b : (Box >) (box 0)] [b∗ : (Box >) b]) typechecking [Pierce and Turner 2000]. Since all top-level defini-
(if (number? (unbox b)) tions are annotated in the above fashion, the type system can prop-
(begin (set-box! b∗ ’no) (unbox b)) agate the latent propositions into non-latent propositions for the
0)) bodies of functions such as strnum?. In short, programmers almost
never need to write down non-latent propositions.
A naive implementation might assign this fragment the type Num-
ber, but its evaluation produces a symbol, because b and b∗ are 7.4 Implementing the Logical System
aliased. To avoid this unsoundness, the unbox procedure is assigned
trivial latent propositions and object. In general, Racket provides The type system presented in section 4 is non-algorithmic. For
structures that are mutable on a per-field basis and that are accessed an implementation, we must both eliminate the subsumption rule
with per-field selectors. Typed Racket therefore does not assign the and implement the T-VAR rule. The former is accomplished via a
selectors of mutable fields any latent propositions or objects. distribution of the subtyping obligations among the rules. The latter
Assignable variables are a second concern in the same realm. demands the implementation of a notion of provability.
If a variable is the target of an assignment, the L-U PDATE rule is Since propositional satisfiability is decidable, the logical sys-
unsound for this variable. Hence, Typed Racket uses a simple anal- tem is straightforward to implement in principle. We employ three
ysis to find all assignment statements and to disable the L-U PDATE strategies to avoid paying the full cost of deciding the logic in al-
rule for those. Since variables are assignable only in the module most all cases. First, we split the type environment from the propo-
that declares them, the analysis is modular and straightforward. sition environment. This separation avoids invoking the logic to
typecheck each variable reference. Second, Typed Racket eagerly
7.2 Multiple Arguments, Multiple Values simplifies logical formulas, significantly decreasing their typical
size. Third, it also refines the type environment each time a new
All λTR functions consume one argument and produce one value. formula is added to the proposition environment. These optimiza-
In contrast, Racket functions are multi-ary and may produce multi- tions mean that most code does not pay the cost of the proof system.
ple values [Flatt and PLT 2010]. For example, the function These techniques are well-known from work on SAT solvers.
(define two-val? Since deciding the logical inference rules of λTR can be cast as a
(λ ([x : >] [y : >]) (values (number? x) (string? y)))) satisfiability-modulo-theories problem, we plan to investigate ap-
plying existing off-the-shelf SMT solvers [Ganzinger et al. 2004].
determines both whether its first argument is a number, and
whether its second argument is a string. 8. Empirical Measurements
Expressing this form of reasoning in our type system demands
a different representation of function types. On the domain side, no Numerous encounters with difficult-to-type idioms in Racket code
additional work is needed because propositions and objects refer triggered the development of our new system. In order to mea-
directly to the names of the parameters. On the range side, each sure its actual effectiveness in comparison with the original sys-
function produces a sequence of values, each of which comes with tem, we inspected the existing Racket code base and measured the
a type, two latent propositions, and a latent object. In our running frequency of certain idioms in practice.
example, the latent propositions for the first return value are Nx |Nx . Since precise measurement of programming idioms is impossi-
Although test positions cannot cope with multiple values, the ble, this section begins with a detailed explanation of our empirical
following idioms exploits multiple values for tests: approach and its limitations. The following two subsections report
on the measurements for the two most important idioms that moti-
(let-values ([(x y) (two-val? e1 e2 )]) (if x — —)) vate the Typed Racket work: those that involve predicates applied
to selectors, as in example 10, and those that involve combinations
Our new function type representation allows the type system to of predicates, as in example 4. In both cases, our results suggest
prove e1 is a number in the then branch of the if expression, and to that our new approach to occurrence typing greatly improves our
use y in a later test expression for checking if e2 is a string. capability to enrich existing Racket code with types.
7.3 User Presentation 8.1 Methodology
While the Typed Racket types capture much useful information Measuring the usefulness of Typed Racket for typing existing code
about the program, they can also be hard for programmers to read presents both opportunities and challenges. The major opportunity
and write. Fortunately, in most cases a simplified type presentation is that the existing Racket code base provides 650,000 lines of code
suffices, and complex types are reserved for special occasions. on which to test both our hypotheses about existing code and our
First, few type errors involve types with non-trivial propositions type system. The challenge is assessing the use of type system
directly. In our experience with Typed Scheme, almost all type er- features on code that does not typecheck.
rors are directly explicable with the underlying, non-occurrence Since the purpose of Typed Racket is to convert existing un-
typing portion of the system, meaning that users’ primary expe- typed Racket programs, it is vital to confirm its usefulness on exist-
rience with occurrence typing is that it just works. ing code. Our primary strategy for assessing the usefulness of our
Second, when users need to specify or read types with proposi- type system has been the porting of existing code, which is the ul-
tions or objects, these are primarily latent and symmetric between timate test of the ability of Typed Racket to follow the reasoning
of Racket programmers. However, Typed Racket does not operate be user-defined type predicates. Each of these uses requires the
on untyped code; it requires type annotations on all functions and extension for local binding described in section 5.2, as well as the
user-defined structures. Therefore, it is not possible to simply apply more general logical reasoning framework of this paper to generate
our new implementation to existing untyped code. the correct filters.
Instead, we have applied less exact techniques to empirically
validate the usefulness of our extensions to Typed Racket. Starting 9. Related Work
from the knowledge of particular type predicates, selectors, and
patterns of logical combinations, we searched for occurrences of Intensional Polymorphism Languages with intensional polymor-
the relevant idioms in the existing code base. We then randomly phism [Crary et al. 1998] also offer introspective operations, e.g.,
sampled these results and analyzed the code fragments in detail; typecase, allowing programs to dispatch on type of the data pro-
this allows us to discover whether the techniques of occurrence vided to functions. The λTR calculus provides significantly greater
typing are useful for the fragment under consideration. flexibility. In particular, it is able to use predicates applied to se-
This approach has two major drawbacks. First, it only allows lectors, reason about combinations of tests, abstract over type tests,
us to count a known set of predicates, selectors, idioms, and other use both the positive and negative results of tests, and use logical
forms. Whether a function is a selector or a type predicate could formulas to enhance the expressiveness of the system. In terms of
only be answered with a semantics-based search, which is currently our examples, the system of Crary et al. could only handle the first.
not available. Second, our approach does not determine if a pro- Generalized Algebraic Data Types Generalized algebraic data
gram would indeed typecheck under Typed Racket, merely that the types [Peyton Jones et al. 2006] are an extension to algebraic data
features we have outlined are indeed necessary. Further limitations types in which “pattern matching causes type refinement.” This is
may be discovered in the future, requiring additional extensions. sometimes presented as a system of type constraints, in addition to
However, despite these drawbacks, we believe that empirical study the standard type environment, as in the HMG(X) and LHM(X)
of the features of Typed Racket is useful. It has already alerted us systems [Simonet and Pottier 2007, Vytiniotis et al. 2010].
to uses of occurrence typing that we had not predicted. Such systems are similar to λTR in several ways—they type-
check distinct branches of case expressions with enriched static
8.2 Selectors environments and support general constraint environments from
Our first measurement focuses on uses of three widely used, built-in which new constraints can be derived. The λTR calculus and
selectors: car, cdr, and syntax-e (a selector that extracts expression constraint-based such as HMG(X) differ in two fundamental ways,
representations from a AST node). A search for compositions of however. First, HMG(X), like other GADT systems, relies on pat-
any predicate-like function with any of these selectors yields: tern matching for type refinement, whereas λTR combines con-
ditional expressions and selector applications, allowing forms ab-
1. 254 compositions of built-in predicates to uses of car for which stractions that patterns prohibit. Second, all of these systems work
λTR would assign a non-trivial object; solely on type variables, whereas λTR refines arbitrary types.
2. 567 such applications for cdr; and
3. 285 such applications for syntax-e. Types and Logic Considering types as logical propositions has
a long history, going back to Curry and Howard [Curry and Feys
Counting only known predicate names means that (number? (car 1958, Howard 1980]. In a dependently typed language such as
x)) is counted but neither (unknown? (car y)) or (string? (car (f ))) Agda [Norell 2007], Coq [Bertot and Castéran 2004], or Epi-
are included because (f ) is not known to have a non-trivial object. gram [McBride and McKinna 2004], the relationships we describe
In sum, this measurement produces a total of at least 1106 useful with predicates and objects could be encoded in types, since types
instances for just three selectors composed with known predicates. can contain arbitrary terms, including terms that reference other
A manual inspection of 20 uses each per selector suggests that variables or the expression itself.
the extensions to occurrence typing presented in this paper are The innovation in λTR is to consider propositions that relate
needed for just under half of all cases. Specifically, in the case of types and variables. This allows us to express the relationships
car, seven of 20 uses require occurrence typing; for cdr, nine of needed to typecheck existing Racket code, while keeping the logic
20 benefit; and the same number applies to syntax-e. Additionally, decidable and easy to understand.
in four cases the type correctness of the code would rely on flow-
sensitivity based on predicate tests, but using exceptional control Types and Flow Analysis for Untyped Languages Shivers [1991]
flow rather than conditionals. describes a type recovery analysis—exploited by Wright [1997]
In conclusion, our manual inspection suggests that some 40% to and Flanagan [1999] for soft typing systems—that includes refin-
45% of the 1106 cases found can benefit from extending occurrence ing the type of variables in type tests. This analysis is only for par-
typing to selector expressions, as described in section 5. This mea- ticular predicates, however, and does not support abstraction over
surement together with the numerous user requests for this feature predicates or logical reasoning about combinations of predicates.
justifies the logical extensions for selector-predicate compositions. Similarly, Aiken et al. [1994] describe a type inference system
using conditional types, which refine the types of variables based
8.3 Logical Combinations on patterns in a case expression. Since this system is built on
the use of patterns, abstracting over tests or combining them, as
Since our original system cannot cope with disjunctive combination
in examples 12 or 5 is impossible. Further, the system does not
of propositions, typically expressed using or, measuring or expres-
account for preceding patterns when typing a right-hand side and
sions in the code base is a natural way to confirm the usefulness
thus cannot perform logical reasoning as in examples 13 and 14.
of general propositional reasoning for Typed Racket. The source of
Racket contains approximately 4860 uses of the or macro; also, or Types for Untyped Languages There has long been interest in
expressions are expanded more than 2000 times for the compila- static typechecking of untyped code. Thatte [1990] and Henglein
tion of the minimal Racket library, demonstrating that this pattern [1994] both present systems integrating static and dynamic types,
occurs widely in the code base. and Henglein and Rehof [1995] describe a system for automatic
The survey of all or expressions in the code base reveals that translation of untyped Scheme code into ML. These systems did
or is used with 37 different primitive type predicates a total of 474 not take into account the information provided by predicate tests,
times, as well as with a wide variety of other functions that may as described by Henglein and Rehof in the quote from section 1.
In the past few years, this work has been picked up and applied H. Ganzinger, G. Hagen, R. Nieuwenhuis, A. Oliveras, and C. Tinelli.
to existing untyped languages. In addition to Typed Scheme, pro- DPLL(T): Fast Decision Procedures. In 16th International Conference
posals have been made for Ruby [Furr et al. 2009], Thorn [Wrigstad on Computer Aided Verification, CAV’04, volume 3114 of Lecture Notes
et al. 2010], JavaScript [ECMA 2007], and others, and theoretical in Computer Science, pages 175–188. Springer-Verlag, 2004.
studies have been conducted by Siek and Taha [2006] and Wadler F. Henglein. Dynamic typing: Syntax and proof theory. Sci. Comput.
and Findler [2009]. To our knowledge, none have yet incorporated Programming, 22(3):197–230, 1994.
occurrence typing or other means of handling predicate tests, al- F. Henglein and J. Rehof. Safe polymorphic type inference for a dynami-
though the authors of DRuby have stated that occurrence typing is cally typed language: translating Scheme to ML. In Proc. Seventh Inter-
their most significant missing feature [priv. comm.]. national Conference on Functional Programming Languages and Com-
puter Architecture, pages 192–203. ACM Press, 1995.
Semantic Subtyping Bierman et al. [2010] present Dminor, a sys- W. A. Howard. The formulas-as-types notion of construction. In J. P. Seldin
tem with a rule for conditionals similar to T-I F. Their system sup- and J. Hindley, editors, To H. B. Curry: Essays on Combinatory Logic,
ports extremely expressive refinement types, with subtyping deter- Lambda Calculus, and Formalism, pages 479–490. Academic Press,
mined by an SMT solver. However, while λTR supports higher- 1980.
order use of type tests, due to the limitations of the semantics sub- R. Komondoor, G. Ramalingam, S. Chandra, and J. Field. Dependent types
typing framework, Dminor is restricted to first order programs. for program understanding. In Tools and Algorithms for the Construction
and Analysis of Systems, volume 3440 of Lecture Notes in Computer
10. Conclusion Science, pages 157–173. Springer-Verlag, 2005.
This paper describes a new framework for occurrence typing. The J. M. Lucassen and D. K. Gifford. Polymorphic effect systems. In Proc.
15th Symposium on Principles of Programming Languages, pages 47–
two key ideas are to derive general propositions from expressions 57. ACM Press, 1988.
and to replace the type environment with a propositions environ-
ment. These ideas increase the type system’s expressive power via C. McBride and J. McKinna. The view from the left. Journal of Functional
Programming, 14(1):69–111, 2004.
reasoning tools from propositional logic.
U. Norell. Towards a practical programming language based on dependent
Acknowledgements type theory. PhD thesis, Chalmers University of Technology, 2007.
Discussions with Aaron Turon greatly improved this paper. The de- S. Peyton Jones, D. Vytiniotis, S. Weirich, and G. Washburn. Simple
velopment of Typed Racket has been supported by Stevie Strick- unification-based type inference for GADTs. In Proc. Eleventh Inter-
land, Eli Barzilay, Hari Prashanth K R, Vincent St-Amour, Ryan national Conference on Functional Programming, pages 50–61. ACM
Press, 2006.
Culpepper and many others. Jed Davis provided assistance with
Coq. B. C. Pierce and D. N. Turner. Local type inference. ACM Trans. Progr.
Lang. Sys., 22(1):1–44, 2000.
References J. C. Reynolds. Automatic computation of data set definitions. In IFIP
Congress (1), pages 456–461, 1968.
A. Aiken, E. L. Wimmers, and T. K. Lakshman. Soft typing with condi-
tional types. In Proc. 21st Symposium on Principles of Programming O. Shivers. Control-Flow Analysis of Higher-Order Languages or Taming
Languages, pages 163–173. ACM Press, 1994. Lambda. PhD thesis, Carnegie Mellon University, Pittsburgh, Pennsyl-
vania, 1991.
Y. Bertot and P. Castéran. Interactive Theorem Proving and Program
Development, volume XXV of EATCS Texts in Theoretical Computer J. G. Siek and W. Taha. Gradual typing for functional languages. In Sev-
Science. Springer-Verlag, 2004. enth Workshop on Scheme and Functional Programming, University of
Chicago Technical Report TR-2006-06, pages 81–92, September 2006.
G. M. Bierman, A. D. Gordon, C. Hricu, and D. Langworthy. Semantic sub-
typing with an SMT solver. In Proc. Fifteenth International Conference V. Simonet and F. Pottier. A constraint-based approach to guarded algebraic
on Functional Programming. ACM Press, 2010. data types. ACM Trans. Progr. Lang. Sys., 29(1):1–54, 2007.
R. Cartwright. User-defined data types as an aid to verifying LISP pro- S. Thatte. Quasi-static typing. In Proc. 17th Symposium on Principles of
grams. In International Conference on Automata, Languages and Pro- Programming Languages, pages 367–381. ACM Press, 1990.
gramming, pages 228–256, 1976. S. Tobin-Hochstadt and M. Felleisen. The design and implementation of
K. Crary, S. Weirich, and G. Morrisett. Intensional polymorphism in Typed Scheme. In Proc. 35th Symposium on Principles of Programming
type-erasure semantics. In Proc. Third International Conference on Languages, pages 395–406. ACM Press, 2008.
Functional Programming, pages 301–312. ACM Press, 1998. D. Vytiniotis, S. Peyton Jones, and T. Schrijvers. Let should not be
H. Curry and R. Feys. Combinatory Logic, volume I. North-Holland, 1958. generalized. In TLDI ’10: Proc. 5th workshop on Types in language
design and implementation, pages 39–50. ACM Press, 2010.
ECMA. ECMAScript Edition 4 group wiki, 2007. URL
http://wiki.ecmascript.org/. P. Wadler and R. B. Findler. Well-typed programs can’t be blamed. In ESOP
’09: Proc. Eighteenth European Symposium on Programming, volume
M. Felleisen, R. B. Findler, M. Flatt, and S. Krishnamurthi. How to Design
5502 of Lecture Notes in Computer Science, pages 1–16. Springer-
Programs. MIT Press, 2001. URL http://www.htdp.org/.
Verlag, 2009.
C. Flanagan and M. Felleisen. Componential set-based analysis. ACM
A. K. Wright and R. Cartwright. A practical soft type system for Scheme.
Trans. Progr. Lang. Sys., 21(2):370–416, 1999.
ACM Trans. Progr. Lang. Sys., 19(1):87–152, 1997.
M. Flatt and PLT. Reference: Racket. Reference Manual
T. Wrigstad, F. Z. Nardelli, S. Lebresne, J. Östlund, and J. Vitek. Integrating
PLT-TR2010-reference-v5.0, PLT Scheme Inc., January 2010.
typed and untyped code in a scripting language. In Proc. 37th Sympo-
http://plt-scheme.org/techreports/.
sium on Principles of Programming Languages, pages 377–388. ACM
M. Furr, J.-h. D. An, J. S. Foster, and M. Hicks. Static type inference for Press, 2010.
ruby. In SAC ’09: Proc. 2009 ACM Symposium on Applied Computing,
pages 1859–1866. ACM Press, 2009.