Download Type Inference - Programming Languages - Study Guide | COMP 311 and more Papers Programming Languages in PDF only on Docsity!
Comp 311 – Type Inference Study Guide
Corky Cartwright
Produced: December 2, 2005
1 Synopsis of Implicitly Polymorphic Jam
The syntax of (Implicitly) Polymorphic Jam is a restriction of the syntax of untyped Jam.
Every legal Polymorphic Jam program is also a legal untyped Jam Program. But the converse
is false, because there may not be a valid typing for a given untyped Jam program.
1.1 Abstract Syntax
The following grammar describes the abstract syntax of Polymorphic Jam. Each clause in the
grammar corresponds directly to a node in the abstract syntax tree. The let construction has
been limited to a single binding for the sake of notational simplicity. It is straightforward to
generalize the rule to multiple bindings (with mutual recursion). Note that let is recursive.
M ::= M (M · · · M ) | P (M · · · M ) | if M then M else M | let x := M in M | V V ::= map x · · · x to M | x | n | true | false | null n ::= 1 | 2 |... P ::= cons | first | rest | null? | cons? | + | - | / | * | = | < | <= | <- | + | - | ~ | ref |! x ::= variable names
In the preceding grammar, unary and binary operators are treated exactly like primitive
functions.
Monomorphic types in the language are defined by τ , below. Polymorphic types are
defined by σ. The → corresponds to a function type, whose inputs are to the left of the
arrow and whose output is to the right of the arrow.
σ ::= ∀α 1 · · · αn. τ τ ::= int | bool | unit | τ 1 × · · · × τn → τ | α | list τ | ref τ α ::= type variable names (usually greek letters)
1.2 Type Checking Rules
In the following rules, the notation Γ[x 1 : τ 1 ,... , xn : τn] means the Γ \ {x 1 ,... , xn} ∪
{x 1 : τ 1 ,... , xn : τn} and Γ′^ abbreviates Γ[x 1 : τ 1 ′,... , xn : τ n′]. Note that Γ \ {x 1 ,... , xn}
means Γ less the type assertions (if any) for {x 1 ,... , xn}.
Γ[x 1 : τ 1 ,... , xn : τn] M : τ Γ map x 1... xn to M : τ 1 × · · · × τn → τ
[abs]
Γ M : τ 1 × · · · × τn → τ Γ M 1 : τ 1 · · · Γ Mn : τn Γ M (M 1 · · · Mn) : τ
[app]
Γ M 1 : bool Γ M 2 : τ Γ M 3 : τ Γ if M 1 then M 2 else M 3 : τ
[if]
Note that there are two rules for let expressions. The [letmono] rule corresponds to the
let rule of Typed Jam; it places no restriction on the form of the right-hand side M 1 of the
let binding. The [letpoly] rule generalizes the free type variables (not occurring in the type
environment Γ) in the type inferred for the right-hand-side of a let binding – provided that
the right-hand-side M 1 is a syntactic value: a constant like null or cons, a map expression, or
a variable. Syntactic values are expressions whose evaluation is trivial, excluding evaluations
that allocate storage.
Γ[x : τ ] ` x : τ
Γ′^ M 1 : τ 1 ′... Γ′^ Mn : τ (^) n′ Γ′^ M : τ Γ let x 1 := M 1 ;.. .; xn := Mn; in M : τ
[letmono]
Γ′^ M 1 : τ 1 ′... Γ′^ Mn : τ (^) n′ Γ[x 1 : CM 1 (τ 1 ′, Γ),... , xn : CMn (τ (^) n′, Γ)] M : τ Γ let x 1 := M 1 ;.. .; xn := Mn; in M : τ
[letpoly]
Γ[x : ∀α 1 ,... , αn. τ ] ` x : O(∀α 1 ,... , αn. τ, τ 1 ,... , τn)
The functions O(·, ·) and C·(·, ·) are the keys to polymorphism. Here is how C·(·, ·) is
defined:
CV (τ, Γ) := ∀{FTV(τ ) − FTV(Γ)}. τ
CN (τ, Γ) := τ
where V is a syntactic value, N is an expression that is not a syntactic value, and FTV(α)
means the “free type variables in the expression (or type environment) α”.
When closing over a type, you must find all of the free variables in τ that are not free
in any of the types in the environment Γ. Then, build a polymorphic type by quantifying τ
over all of those type variables.
To open a polymorphic type
∀α 1 ,... , αn. τ,
substitute any type terms τ 1 ,... , τn for the quantified type variables α 1 ,... , αn:
O(∀α 1 ,... , αn. τ, τ 1 ,... , τn) = τ[α 1 :=τ 1 ,...,αn:=τn]
which creates a monomorphic type from a polymorphic type. For example,
O(∀α. α → α, τ ) = τ → τ
Task 2: Are the following Polymorphic Jam programs typable? Justify your answer either
by giving a proof tree (constructed using the inference rules for PolyJam) or by showing a
conflict in the type constraints generated by matching the inference rules against the program
text.
1. let listMap := map f,l to
if null?(l) then null else cons(f(first(l)), listMap(f, rest(l))) in listMap(first,null);
let length := map l to if null?(l) then 0 else 1 + length(rest(l)); l := cons(cons(1,null),cons(cons(2,cons(3,null)),null)); in length(l)+length(first(l))
Task 3: Give a simple example of an untyped Jam expression that is not typable in Typed
Jam but is typable in Polymorphic Jam.
3 Solutions to Selected Exercises
Task 1 : The first four expressions are typable in Typed Jam, but the fifth is not.
- Tree 1: Γ 0 [f:int → int]
10:int Γ 0 [f:int → int] f:int → int Γ 0 [f:int → int] ` f(10):int
[app]
Γ 0 ` map f to f(10):(int → int) → int
[abs]
Tree 2: Tree 1
Γ 0 [x:int] x:int Γ 0 map x to x:int → int
[abs]
Γ 0 ` (map f to f(10))(map x to x):int
[app]
- Type Inference Proof Omitted.
- Tree 1: Γ 0 [x:int]
/:int × int → int Γ 0 [x:int] 1:int Γ 0 [x:int] x:int Γ 0 [x:int] 1/x:int]
[app]
Tree 2: Γ 0 [x:int] +:int × int → int Γ 0 [x:int] 1:int Tree 1 Γ 0 [x:int] ` (1 + (1/x)):int
[app]
Γ 0 ` (map x to 1 + (1/x)):int → int
[abs]
Tree 3: Tree 2 Γ 0 0:int Γ 0 (map x to 1 + (1 /x))(0):int
[app]
- Tree 1: Γ 0 [x:int → int]
x:int → int Γ 0 (map x to x):(int → int) → (int → int)
[abs]
Tree 2: Γ 0 [y:int] y:int Γ 0 (map y to y):int → int
[abs]
Tree 3: Tree 1 Tree 2 Γ 0 ` (map x to x)(map y to y):int → int
[app]
- This example is almost identical to the previous one, but the identity function id is defined only once in a let binding and then applied to itself. Since Typed Jam does not support polymorphism, we can only assign one typing to id. But we needed two different typings for the identity in the preceding example, so we cannot type this program.
Task 2: Both programs are typable in Polymorphic Jam. In fact the first program is
typable in Typed Jam because the length function is only applied to one type of list. Hence
the letmono rule can be used to type the let expression in this program instead of the more
general letpoly rule.
1. Type Inference Proof Omitted.
2. Let Γ 1 abbreviate Γ 0 [length : list α → int, l : list list int];
let Γ 2 abbreviate Γ 0 [length : ∀α. (list α → int), l : list list int];
and let Γ 3 abbreviate Γ 1 [l : list α].
Tree 1: Γ 3 rest : list α → list α Γ 3 l : list α Γ 3 ` rest(l) : list α
[app] Γ 3 ` length : list α → int
Γ 3 ` length(rest(l)) : int
[app]
Tree 2: Γ 3 + : int × int → int Γ 3 1:int Tree 1 Γ 3 ` 1+length(rest(l)) : int
[app]
Tree 3: Γ 3 null? : list α → bool Γ 3 l : list α Γ 3 ` null?(l) : bool
[app] Γ 3 ` 0 : int Tree 2
Γ 3 ` if null?(l) then 0 else 1+length(rest(l)) : int
[if]
Γ 2 ` map l to if null?(l) then 0 else 1+length(rest(l)) : int
[abs]