Title: Lecture
1Lecture 14, Feb. 28, 2007
- Writing type-checkers in ML
- Role of type rules
- Representing types
- Representing programs
- Handling errors
- Guessing types
- Declarations
- Let expressions
2Type Checkers in ML
- To build type-checkers in ML we need
- A datatype to represent types
- A datatype (or datatypes) to represent programs
(whose type we are checking) - Expressions
- Statements
- Declarations
- A means for computing equality over types
- A means for expressing type errors
- We build type checkers as attribute computations
- We need to think ahead.
- An attribute computation for each datatype
representing programs - What are the inherited and synthesized attributes
for each datatype - What will the type of the functions that
implement each attribute computation. - Will we simply compute a type, or will we
decorate the syntax tree?
3The role of type rules
- Type rules play an important role.
- They are short and concise
- They are used as a communication mechanism.
- S - x a
- S - f a -gt t
- ------------------
- S - f x t
- Normally, the context is an inherited attribute,
and the result is a synthesized attribute
4Representing Types
- datatype MLtype
- Unit
- Int
- Char
- Bool
- Product of MLtype list
- Arrow of (MLtype MLtype)
5Representing Programs 1
- datatype Op Plus Less And
- datatype Constant
- Cint of int ( 5 )
- Cchar of char ( z )
- Cbool of bool ( true )
- and Dec
- Valdec of stringExp
- ( let val x 5 in x end )
- Fundec of stringstringMLtypeExp
- ( let fun f (xint) x 1 in f end )
6Representing Programs 2
- datatype Exp
- Lit of Constant ( 5 )
- Var of string ( x )
- App of ExpExp ( f x )
- Tuple of Exp list ( (x,3,true) )
- Infix of ExpOpExp ( x3 )
- Stmt of Exp list ( (print x 3) )
- If of Exp Exp Exp ( if x then y else 3
) - While of Exp Exp ( while x do (f x) )
- Anonfun of string MLtype Exp
- ( (fn x gt x1) )
- Let of DecExp ( let val x 1 in x end )
7Type Equality
- fun typeeq (x,y)
- case (x,y) of
- (Void,Void) gt true
- (Int,Int) gt true
- (Char,Char) gt true
- (Bool,Bool) gt true
- (Arrow(d1,r1),Arrow(d2,r2)) gt typeeq(d1,d2)
andalso - typeeq(r1,r2)
- (Product(ss),Product(ts)) gt (listeq ss ts)
- (_,_) gt false
- and listeq (xxs) (yys)
- typeeq(x,y) andalso listeq xs ys
- listeq true
- listeq _ _ false
8Expressing Errors
- In ML we are lucky to have a rich exception
mechanism. - We could use one exception for each kind of type
error. - Or we could have a general purpose exception that
carried specific information about the error. - Or something in between.
- We use the second approach
- exception TypeError of Expstring
- fun error e s raise(TypeError (e,s))
9Functions to report errors
- fun showt Unit "()"
- showt Int "int"
- showt Char "char"
- showt Bool "bool"
- showt (Product ts)
- let fun showeach ""
- showeach x showt x
- showeach (xxs)
- (showt x)""(showeach xs)
- in "("(showeach ts)")" end
- fun unexpected r t1 t2
- error r ("Found type "
- (showt t1)
- " expecting type "
- (showt t2))
10Thinking ahead
- We have 4 different datatypes that represent
programs - Op
- Constant
- Exp
- Dec
- What attributes will each have?
- Two kinds of attributes in this case types and
contexts - Types MLtyp
- Context (stringMLtype) list
- Inherited or Synthesized?
- Op synthesized (MLtype
MLtype MLtype) - Constant synthesized MLtype
- Exp inherited (stringMLtype) list
- synthesized MLtype
- Dec inherited (stringMLtype) list
- synthesized ( MLtype
(stringMLtype) list)
11Types of programs implementing the attribute
computations
- TCOp OP -gt (MLtype MLtype MLtype)
- TCConstant Constant -gt MLtype
- TCExp Exp -gt
- (stringMLtype) list -gt
- MLtype
- TCDec Dec -gt
- (stringMLtype) list -gt
- (MLtype (stringMLtype) list)
12Operators
- fun TCOp Plus (Int,Int,Int)
- TCOp Less (Int,Int,Bool)
- TCOp And (Bool,Bool,Bool)
Left argument
Result type
Right argument
13Constants
- fun TCConstant (Cint n) Int
- TCConstant (Cchar c) Char
- TCConstant (Cbool t) Bool
Note that the value of the constant, has nothing
to do with its type. E.g. Both 4 and 12 have
type Int
14Variables
- (S x) t
- ---------------------
- S - x t (where x is a variable)
- fun TCExp x cntxt
- case x of
- Var s gt
- (case List.find (fn (nm,t) gt nms) cntxt of
- SOME(nm,t) gt t
- NONE gt error x "Undeclared variable")
15Constants
- S - n int (where n an integer constant
like 5 or 23) -
- S - c char (where c character constant
like a ) -
- S - b bool (where b boolean like true or
false) - fun TCConstant (Cint n) Int
- TCConstant (Cchar c) Char
- TCConstant (Cbool t) Bool
- fun TCExp x cntxt
- case x of
- Lit c gt TCConstant c
16Infix expressions
S - x t1 S - y t2 (S ltgt) t1 t2 -gt
t3 ----------------------------- where ltgt
is a binary operator like or S - x ltgt y
t3 fun TCExp x cntxt case x of
Infix(l,x,r) gt let val ltype TCExp l
cntxt val rtype TCExp r cntxt
val (lneed,rneed,result) TCOp x in case
(typeeq(ltype,lneed)
,typeeq(rtype,rneed)) of (true,true) gt
result (true,false) gt unexpected r
rtype rneed (false,true) gt unexpected
l ltype lneed (false,false) gt
unexpected l ltype lneed end
Notice how sub expressions are type-checked
first, then the result type is computed from
those results
17The Big picture
- fun TCExp x cntxt
- case x of
- Lit c gt TCConstant c
- Var s gt
- (case List.find (fn (nm,t) gt nms) cntxt of
- SOME(nm,t) gt t
- NONE gt error x "Undeclared variable")
- Infix(l,x,r) gt
- let val ltype TCExp l cntxt
- val rtype TCExp r cntxt
- val (lneed,rneed,result) TCOp x
- in case (typeeq(ltype,lneed)
- ,typeeq(rtype,rneed)) of
- (true,true) gt result
- (true,false) gt unexpected r rtype
rneed - (false,true) gt unexpected l ltype
lneed - (false,false) gt unexpected l ltype
lneed - end
18Function calls
- S - x a
- S - f a -gt t
- ----------------------
- S - f x t
- fun TCExp x cntxt
- case x of
- App(f,x) gt
- let val ftype TCExp f cntxt
- val xtype TCExp x cntxt
- in case ftype of
- Arrow(dom,rng) gt
- if ( typeeq(dom,xtype) )
- then rng
- else unexpected x xtype dom
- other gt error f ("the type "
- showt other
- " is not a
function") - end
Note how the domain of the function must have the
same type as the actual argument.,
19ML statement types
- S - ei ai S en t
- --------------------------------------
- S - (e1 en) t
- fun TCExp x cntxt
- case x of
- Stmt xs gt
- let val xstypes List.map
- (fn x gt TCExp x cntxt) xs
- fun last x x
- last (xxs) last xs
- last error x "Tuple with no
elements" - in last xstypes end
20ML tuples
- S - ei ti
- --------------------------------------
- S - (e1, ,en) (t1 tn)
- fun TCExp x cntxt
- case x of
- Tuple xs gt
- let val xstypes
- List.map (fn x gt TCExp x cntxt) xs
- in Product xstypes end
21If expressions
-
- S - x bool S y t
- S - z t
- ---------------------------------------
- S - if x then y else z t
- fun TCExp x cntxt
- case x of
- If(x,y,z) gt
- let val xtype TCExp x cntxt
- val ytype TCExp y cntxt
- val ztype TCExp z cntxt
- in case xtype of
- Bool gtif (typeeq(ytype,ztype))
- then ytype
- else unexpected y ytype
ztype - other gt error x ("the type "
- showt other
- " is not boolean")
22ML while stmt
- S - e bool S - s a
- ------------------------------
- S - while e do s unit
- fun TCExp x cntxt
- case x of
- While(test,body) gt
- let val ttype TCExp test cntxt
- val btype TCExp body cntxt
- in case ttype of
- Bool gt Unit
- other gt unexpected test ttype Bool
- end
-
23ML anonymous function types
- S(x,a) - e b
- ------------------------------
- S - (fn (xa) gt e) a -gt b
- fun TCExp x cntxt
- case x of
- Anonfun(x,t,body) gt
- let val btype TCExp body ((x,t)cntxt)
- in Arrow(t,btype) end
-
S(x,a) means add the mapping of variable x to
the type a to the table S. If S already has a
mapping for x, then overwrite it with a
Note, how for the first time, the context, which
is an inherited attribute, gets bigger as it
flows down the syntax tree into the body
24Handling declarations
- Declarations are interesting because they have
contexts as both synthesized and inherited
attributes. - Consider
- Let fun f x x 9
- In 3 f 4 end
- The context flows into (fun f x x9), but a
new context also flows out of it with a new
typing for f so that the new context can flow
into (3 f 4) - TCDec Dec -gt
- (stringMLtype) list -gt
- (MLtype (stringMLtype) list)
25- and TCDec (Valdec(nm,exp)) cntxt
- let val nmtype TCExp exp cntxt
- in (nmtype,(nm,nmtype)cntxt) end
- TCDec (Fundec(nm,arg,argtype,body)) cntxt
- let val bodytype TCExp body
- ((arg,argtype)cntx
t) - val nmtype Arrow(argtype,bodytype)
- val newcntxt (nm,nmtype)cntxt
- in (nmtype,newcntxt) end
Note, recursive functions would need a different
strategy
26Let expressions
- fun TCExp x cntxt
- case x of
- Let(d,b) gt
- let val (_,cntxt2) TCDec d cntxt
- val btype TCExp b cntxt2
- in btype end
27Guessing types
- Recall the type constructor for anonymous
functions - Anonfun(x,t,body)
- Note in real ML the type of the argument is not
given. - The type is computed from the context it is used
inside of body. - In order to guess we must initialize the table
with an empty slot, and then fill it in later.
28Represent types
- datatype MLtype
- Unit
- Int
- Char
- Bool
- Product of MLtype list
- Arrow of (MLtype MLtype)
- Tvar of (MLtype option) ref
- Think of the ref as a pointer (ref NONE) is a
null pointer. (ref (SOME x)) is a pointer to x
A mutable reference we can overwrite later.
29We must be careful
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME Int ))
30prune
- fun prune (Tvar(r as (ref(SOME x))))
- let val last prune x
- in r SOME last last end
- prune x x
- Makes all references in a chain point to the very
last type. - Also returns the very last type.
- Will never return a Tvar unless it is unbound.
I.e. it is a reference to NONE
31Implement type equality
- fun typeeq (x,y)
- case (prune x,prune y) of
- (Unit,Unit) gt true
- (Int,Int) gt true
- (Char,Char) gt true
- (Bool,Bool) gt true
- (Arrow(d1,r1),Arrow(d2,r2)) gt
- typeeq(d1,d2) andalso typeeq(r1,r2)
- (Product(ss),Product(ts)) gt (listeq ss ts)
- (Tvar(r as (ref NONE)),t) gt (r SOME t
true) - (t,Tvar(r as (ref NONE))) gt (r SOME t
true) - (_,_) gt false
Overwrite a null pointer once we know what it is
supposed to be equal to.
32Whenever we case over an MLtype
- App(f,x) gt
- let val ftype TCExp f cntxt
- val xtype TCExp x cntxt
- in case prune ftype of
- Arrow(dom,rng) gt
- if (typeeq(dom,xtype))
- then rng
- else unexpected x xtype dom
- other gt
- error f ("the type "
- showt other
- " is not a function")
- end
33A Second try
- fun TCExp x cntxt
- case x of
- Anonfun(x, _ ,body) gt
- let val t Tvar(ref NONE)
- val btype
- TCExp body ((x,t)cntxt)
- in Arrow(prune t,btype) end
Generate a new fresh empty slot. I.e. guess the
type
Hopefully typing the body will force the slot to
be filled in.
Get rid of the indirect reference if there is one
34- CS321 Prog Lang Compilers
Assignment 10 - Assigned Feb. 28, 2006 Due
Wednesday. March 7, 2007 - 1) The goal of this assignment is to extend the
type checker discussed in lecture 14 to handle
refereces and assinments. To do this you will
need to - A) Add a new constructed type to MLtype to
represent reference types. Do this by adding a
new type constructor to MLtype called "Ref". - B) Add two new type constructors to "Exp" called
"Init" and "Assign". They should coorespond to
the underlined ML expressions in the examples
below - val x ref 5
- -------
- val y ( x 6 print x)
- --------
- C) Extend the function "typeeq" to handle the new
"Ref" type. - D) Extend the function "TCExp" to handle the two
new kinds of expressions. The cases should
correspond to the type rules below.