Download presentation
Presentation is loading. Please wait.
1
Cse321, Programming Languages and Compilers 1 7/15/2015 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
2
Cse321, Programming Languages and Compilers 2 7/15/2015 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?
3
Cse321, Programming Languages and Compilers 3 7/15/2015 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
4
Cse321, Programming Languages and Compilers 4 7/15/2015 Representing Types datatype MLtype = Unit | Int | Char | Bool | Product of MLtype list | Arrow of (MLtype * MLtype);
5
Cse321, Programming Languages and Compilers 5 7/15/2015 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 *)
6
Cse321, Programming Languages and Compilers 6 7/15/2015 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 *)
7
Cse321, Programming Languages and Compilers 7 7/15/2015 Type Equality fun 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
8
Cse321, Programming Languages and Compilers 8 7/15/2015 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));
9
Cse321, Programming Languages and Compilers 9 7/15/2015 Functions 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 (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));
10
Cse321, Programming Languages and Compilers 10 7/15/2015 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)
11
Cse321, Programming Languages and Compilers 11 7/15/2015 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)
12
Cse321, Programming Languages and Compilers 12 7/15/2015 Operators fun TCOp Plus = (Int,Int,Int) | TCOp Less = (Int,Int,Bool) | TCOp And = (Bool,Bool,Bool); Left argument Right argument Result type
13
Cse321, Programming Languages and Compilers 13 7/15/2015 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
14
Cse321, Programming Languages and Compilers 14 7/15/2015 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")
15
Cse321, Programming Languages and Compilers 15 7/15/2015 Constants 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 => TCConstant c
16
Cse321, Programming Languages and Compilers 16 7/15/2015 Infix expressions S |- x : t 1 S |- y : t 2 (S )= t 1 * t 2 -> t 3 ----------------------------- where is a binary operator like + or * S |- x y : t 3 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
17
Cse321, Programming Languages and Compilers 17 7/15/2015 The Big picture fun 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
18
Cse321, Programming Languages and Compilers 18 7/15/2015 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.,
19
Cse321, Programming Languages and Compilers 19 7/15/2015 ML statement types S |- e i : a i S |= e n : t -------------------------------------- S |- (e 1 ; … ;e n ) : 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
20
Cse321, Programming Languages and Compilers 20 7/15/2015 ML tuples S |- e i : t i -------------------------------------- S |- (e 1, …,e n ) : (t 1 * … * t n ) fun TCExp x cntxt = case x of | Tuple xs => let val xstypes = List.map (fn x => TCExp x cntxt) xs in Product xstypes end
21
Cse321, Programming Languages and Compilers 21 7/15/2015 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
22
Cse321, Programming Languages and Compilers 22 7/15/2015 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
23
Cse321, Programming Languages and Compilers 23 7/15/2015 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
24
Cse321, Programming Languages and Compilers 24 7/15/2015 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)
25
Cse321, Programming Languages and Compilers 25 7/15/2015 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
26
Cse321, Programming Languages and Compilers 26 7/15/2015 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
27
Cse321, Programming Languages and Compilers 27 7/15/2015 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.
28
Cse321, Programming Languages and Compilers 28 7/15/2015 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.
29
Cse321, Programming Languages and Compilers 29 7/15/2015 We must be careful Tvar(ref (SOME _ )) Tvar(ref (SOME Int )) Tvar(ref (SOME _ )) Tvar(ref (SOME Int ))
30
Cse321, Programming Languages and Compilers 30 7/15/2015 prune 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
31
Cse321, Programming Languages and Compilers 31 7/15/2015 Implement type equality fun 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.
32
Cse321, Programming Languages and Compilers 32 7/15/2015 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
33
Cse321, Programming Languages and Compilers 33 7/15/2015 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
34
Cse321, Programming Languages and Compilers 34 7/15/2015 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. 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.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.