View
215
Download
1
Embed Size (px)
Citation preview
Cse321, Programming Languages and Compilers
104/19/23
Lecture #14, Feb. 28, 2007
• Writing type-checkers in ML
•Role of type rules
•Representing types
•Representing programs
•Handling errors
•Guessing types
•Declarations
•Let expressions
Cse321, Programming Languages and Compilers
204/19/23
Type 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?
Cse321, Programming Languages and Compilers
304/19/23
The 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 -> t
------------------
S |- f x : t
Normally, the context is an inherited attribute, and the result is a synthesized attribute
Cse321, Programming Languages and Compilers
404/19/23
Representing Typesdatatype MLtype =
Unit
| Int
| Char
| Bool
| Product of MLtype list
| Arrow of (MLtype * MLtype);
Cse321, Programming Languages and Compilers
504/19/23
Representing 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 string*Exp
(* let val x = 5 in x end *)
| Fundec of string*string*MLtype*Exp
(* let fun f (x:int) = x + 1 in f end *)
Cse321, Programming Languages and Compilers
604/19/23
Representing Programs 2
datatype Exp
= Lit of Constant (* 5 *)
| Var of string (* x *)
| App of Exp*Exp (* f x *)
| Tuple of Exp list (* (x,3,true) *)
| Infix of Exp*Op*Exp (* x+3 *)
| 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 => x+1) *)
| Let of Dec*Exp (* let val x = 1 in x end *)
Cse321, Programming Languages and Compilers
704/19/23
Type Equalityfun typeeq (x,y) =
case (x,y) of
(Void,Void) => true
| (Int,Int) => true
| (Char,Char) => true
| (Bool,Bool) => true
| (Arrow(d1,r1),Arrow(d2,r2)) => typeeq(d1,d2) andalso
typeeq(r1,r2)
| (Product(ss),Product(ts)) => (listeq ss ts)
| (_,_) => false
and listeq (x::xs) (y::ys) =
typeeq(x,y) andalso listeq xs ys
| listeq [] [] = true
| listeq _ _ = false
Cse321, Programming Languages and Compilers
804/19/23
Expressing Errors
• In ML we are lucky to have a rich exception mechanism.
1. We could use one exception for each kind of type error.
2. Or we could have a general purpose exception that carried specific information about the error.
3. Or something in between.
• We use the second approach
exception TypeError of Exp*string;
fun error e s = raise(TypeError (e,s));
Cse321, Programming Languages and Compilers
904/19/23
Functions to report errorsfun showt Unit = "()" | showt Int = "int" | showt Char = "char" | showt Bool = "bool" | showt (Product ts) = let fun showeach [] = "" | showeach [x] = showt x | showeach (x::xs) = (showt x)^"*"^(showeach xs) in "("^(showeach ts)^")" end;
fun unexpected r t1 t2 = error r ("Found type "^ (showt t1)^ " expecting type "^ (showt t2));
Cse321, Programming Languages and Compilers
1004/19/23
Thinking 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 === (string*MLtype) list
• Inherited or Synthesized?– Op synthesized: (MLtype * MLtype * MLtype)
– Constant synthesized: MLtype
– Exp inherited (string*MLtype) list
synthesized: MLtype
– Dec inherited (string*MLtype) list
synthesized: ( MLtype * (string*MLtype) list)
Cse321, Programming Languages and Compilers
1104/19/23
Types of programs implementing the attribute computations
• TCOp:: OP -> (MLtype * MLtype * MLtype)
• TCConstant:: Constant -> MLtype
• TCExp Exp ->
(string*MLtype) list ->
MLtype
• TCDec:: Dec ->
(string*MLtype) list ->
(MLtype * (string*MLtype) list)
Cse321, Programming Languages and Compilers
1204/19/23
Operators
fun TCOp Plus = (Int,Int,Int)
| TCOp Less = (Int,Int,Bool)
| TCOp And = (Bool,Bool,Bool);
Left argument
Right argument
Result type
Cse321, Programming Languages and Compilers
1304/19/23
Constants
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
Cse321, Programming Languages and Compilers
1404/19/23
Variables
(S x) = t --------------------- S |- x : t (where x = is a variable)
fun TCExp x cntxt =
case x of
Var s =>
(case List.find (fn (nm,t) => nm=s) cntxt of
SOME(nm,t) => t
| NONE => error x "Undeclared variable")
Cse321, Programming Languages and Compilers
1504/19/23
ConstantsS |- 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 => TCConstant c
Cse321, Programming Languages and Compilers
1604/19/23
Infix expressions
S |- x : t1 S |- y : t2
(S <+>)= t1 * t2 -> t3
----------------------------- where <+> is a binary operator like + or *S |- x <+> y : t3
fun TCExp x cntxt = case x of| Infix(l,x,r) => 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) => result | (true,false) => unexpected r rtype rneed | (false,true) => unexpected l ltype lneed | (false,false) => unexpected l ltype lneed end
Notice how sub expressions are type-checked first, then the result type is computed
from those results
Cse321, Programming Languages and Compilers
1704/19/23
The Big picturefun TCExp x cntxt = case x of Lit c => TCConstant c | Var s => (case List.find (fn (nm,t) => nm=s) cntxt of SOME(nm,t) => t | NONE => error x "Undeclared variable") | Infix(l,x,r) => 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) => result | (true,false) => unexpected r rtype rneed | (false,true) => unexpected l ltype lneed | (false,false) => unexpected l ltype lneed end
Cse321, Programming Languages and Compilers
1804/19/23
Function calls S |- x : a S |- f : a -> t---------------------- S |- f x : t
fun TCExp x cntxt = case x of| App(f,x) => let val ftype = TCExp f cntxt val xtype = TCExp x cntxt in case ftype of Arrow(dom,rng) => if ( typeeq(dom,xtype) ) then rng else unexpected x xtype dom | other => 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.,
Cse321, Programming Languages and Compilers
1904/19/23
ML statement typesS |- ei : ai S |= en : t
--------------------------------------
S |- (e1; … ;en) : t
fun TCExp x cntxt =
case x of
| Stmt xs =>
let val xstypes = List.map
(fn x => TCExp x cntxt) xs
fun last [x] = x
| last (x::xs) = last xs
| last [] = error x "Tuple with no elements"
in last xstypes end
Cse321, Programming Languages and Compilers
2004/19/23
ML tuples
S |- ei : ti
--------------------------------------
S |- (e1, … ,en) : (t1 * … * tn)
fun TCExp x cntxt =
case x of
| Tuple xs =>
let val xstypes =
List.map (fn x => TCExp x cntxt) xs
in Product xstypes end
Cse321, Programming Languages and Compilers
2104/19/23
If 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) => let val xtype = TCExp x cntxt val ytype = TCExp y cntxt val ztype = TCExp z cntxt in case xtype of Bool =>if (typeeq(ytype,ztype)) then ytype else unexpected y ytype ztype | other => error x ("the type "^ showt other^ " is not boolean") end
Cse321, Programming Languages and Compilers
2204/19/23
ML while stmt
S |- e : bool S |- s : a ------------------------------ S |- while e do s : unit
fun TCExp x cntxt = case x of| While(test,body) => let val ttype = TCExp test cntxt val btype = TCExp body cntxt in case ttype of Bool => Unit | other => unexpected test ttype Bool end
Cse321, Programming Languages and Compilers
2304/19/23
ML anonymous function types
S+(x,a) |- e : b ------------------------------ S |- (fn (x:a) => e) : a -> b
fun TCExp x cntxt = case x of | Anonfun(x,t,body) => 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
Cse321, Programming Languages and Compilers
2404/19/23
Handling 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 = x+9), 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 ->
(string*MLtype) list ->
(MLtype * (string*MLtype) list)
Cse321, Programming Languages and Compilers
2504/19/23
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)::cntxt)
val nmtype = Arrow(argtype,bodytype)
val newcntxt = (nm,nmtype)::cntxt
in (nmtype,newcntxt) end
Note, recursive functions would need a
different strategy
Cse321, Programming Languages and Compilers
2604/19/23
Let expressions
fun TCExp x cntxt =
case x of
| Let(d,b) =>
let val (_,cntxt2) = TCDec d cntxt
val btype = TCExp b cntxt2
in btype end
Cse321, Programming Languages and Compilers
2704/19/23
Guessing 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.
Cse321, Programming Languages and Compilers
2804/19/23
Represent 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.
Cse321, Programming Languages and Compilers
2904/19/23
We must be careful
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME Int ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME _ ))
Tvar(ref (SOME Int ))
Cse321, Programming Languages and Compilers
3004/19/23
prunefun 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
Cse321, Programming Languages and Compilers
3104/19/23
Implement type equalityfun typeeq (x,y) =
case (prune x,prune y) of
(Unit,Unit) => true
| (Int,Int) => true
| (Char,Char) => true
| (Bool,Bool) => true
| (Arrow(d1,r1),Arrow(d2,r2)) =>
typeeq(d1,d2) andalso typeeq(r1,r2)
| (Product(ss),Product(ts)) => (listeq ss ts)
| (Tvar(r as (ref NONE)),t) => (r := SOME t; true)
| (t,Tvar(r as (ref NONE))) => (r := SOME t; true)
| (_,_) => false
Overwrite a null pointer once we know what it is supposed to
be equal to.
Cse321, Programming Languages and Compilers
3204/19/23
Whenever we case over an MLtype| App(f,x) => let val ftype = TCExp f cntxt val xtype = TCExp x cntxt in case prune ftype of Arrow(dom,rng) => if (typeeq(dom,xtype)) then rng else unexpected x xtype dom | other => error f ("the type "^ showt other^ " is not a function") end
Cse321, Programming Languages and Compilers
3304/19/23
A Second try
fun TCExp x cntxt =
case x of
| Anonfun(x, _ ,body) =>
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
Cse321, Programming Languages and Compilers
3404/19/23
CS321 Prog Lang & Compilers Assignment #10Assigned: 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.
S |- x : t ref S |- y : t--------------------------------- S |- x := y : unit
S |- x : t------------------------ S |- ref x : t ref
You may copy and paste the code from the lecture to get started. Test your code, and highlight all the additions to the lecture code you made in what you hand in.