22c:054 Team Projects

ML Type Checker (Prolog, Oz)

Implement a type checker for an ML-like language. The type checker is a predicate TypeOf(E,T) which, given an ML expression E and an unbound (Prolog, Oz) variable T, succeeds binding the type of E to T if E is well-typed and fails otherwise.
Your implementation should be such that TypeOf(E,T) succeeds even when T is previously bound to some (possibly) parametric type t, provided that the computed type of E and t have a common instance.

The type checker knows the type of the primitive function and constant symbols, which are just:

::, nil, hd, tl, +, *, ~, 0, 1, true, false, =, <, >, first, second.

Given an ML expression e over those predefined symbols, it is able to infer/check the type of e.

ML Type System

Your type checker must follow the types derivation rules and facts that define the type system of languages such as SML or Miranda. Such rules and facts are described below, with the following conventions:

For a concrete example of the conventions above, the notation

x1 : real,  x2 : int,  x : real->int->real  |-  (x x1 x2) : real

for instance, says that the espression (x x1 x2) has type real under the assumptions that x has type real->int->real, x1 has type real, and x2 has type int.

Type Rules

Pair Construction:
A |- e1 : T1     A |- e2 : T2
-------------------
A |- (e1, e2) : T1*T2

The expression (e1, e2) has type T1*T2 under the type assumptions A if e1 has type T1 under the type assumptions A and e2 has type T2 under the type assumptions A.

List Construction:
A |- e1 : T     A |- e2 : T list
--------------------
A |- e1::e2 : T list

The expression e1::e2 has type T list under the type assumptions A if e1 has type T under the type assumptions A and e2 has type T list under the type assumptions A.

Lambda Abstraction:
A, x:T1 |- e : T2
----------------------
A |- (fn (x:T1) => e) : T1 -> T2

The expression fn (x:T1) => e has type T1 -> T2 under the type assumptions A if e has type T2 under the type assumptions A, x:T1.

Function Application:
A |- e2 : T1->T2     A |- e1 : T1
-----------------------
A |- (e2 e1) : T2

The expression (e2 e1) has type T2 under the type assumptions A if e2 has type T1 -> T2 under the type assumptions A, e1 type T1 under the type assumptions A.

Conditional:
A |- e1 : bool     A |- e2 : T     A |- e3 : T
-----------------------------
A |- (if e1 then e2 else e3) : T

The expression if e1 then e2 else e3 has type T under the type assumptions A if e1 has type bool under the type assumptions A and e2 and e3 have type T under the type assumptions A.

Type Facts

A type fact is a type assumption that one always makes in the type system in question.
These are the type facts of our ML-like language, stating the type of the primitive constant/function symbols in the language.

0 : int,  1 : int,  + : (int*int) -> int,  * : (int*int) -> int,  ~ : int -> int,
true : bool,  false : bool,
= : (T*T) -> bool,
> : (int*int) -> bool,  < : (int*int) -> bool,
nil : T list,  hd : T list -> T,  tl : T list -> T list,
first : T1*T2 -> T1,  second : T1*T2 -> T2.

(the functions first and second respectively return the first and the second element of a pair.)

Implementation Details (Prolog)

For simplicity, conform to the restrictions below on the language.

According to the conventions above, the ML expression

((fn (n:int) => if n>0 then n+n else 0) (hd [4,1]))
for instance, is written as
fn(var(n,int), if(>#(n,0), +#(n,n), 0))#(hd#(4::1::nil))

Similarly, the ML expression

'a -> 'b*'c -> 'c list
is written as
A arrow B*C arrow C list

If you start your implementation with the Prolog code in this file, expressions such as fn(var(n,int), if(>#(n,0), +#(n,n), 0))#(hd#(4::1::nil)) or A arrow B*C arrow C list become legal Prolog terms, which then can be input directly into Prolog.
This will save you the effort of writing a parser for the language.

Hints: Be careful with the Lambda Abstraction rule.

 

Implementation Details (Oz)

Same as for the Prolog implementation, except for the following:


Last Updated: Mar 8, 2000