Cse321, Programming Languages and Compilers Lecture #14, Feb. 28, 2007 • Writing type-checkers in ML •Role of type rules •Representing types •Representing programs •Handling errors •Guessing types •Declarations •Let.

Download Report

Transcript Cse321, Programming Languages and Compilers Lecture #14, Feb. 28, 2007 • Writing type-checkers in ML •Role of type rules •Representing types •Representing programs •Handling errors •Guessing types •Declarations •Let.

Cse321, Programming Languages and Compilers
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
11/7/2015
1
Cse321, Programming Languages and Compilers
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?
11/7/2015
2
Cse321, Programming Languages and Compilers
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
11/7/2015
3
Cse321, Programming Languages and Compilers
Representing Types
datatype MLtype =
Unit
| Int
| Char
| Bool
| Product of MLtype list
| Arrow of (MLtype * MLtype);
11/7/2015
4
Cse321, Programming Languages and Compilers
Representing Programs 1
datatype Op = Plus | Less | And
datatype Constant
= Cint of int
| Cchar of char
| Cbool of bool
(* 5 *)
(* #”z” *)
(* 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 *)
11/7/2015
5
Cse321, Programming Languages and Compilers
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
11/7/2015
6
*)
*)
*)
*)
*)
*)
*)
*)
*)
*)
Cse321, Programming Languages and Compilers
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)
typeeq(r1,r2)
| (Product(ss),Product(ts)) => (listeq ss ts)
| (_,_) => false
andalso
and listeq (x::xs) (y::ys) =
typeeq(x,y) andalso listeq xs ys
| listeq [] [] = true
| listeq _ _ = false
11/7/2015
7
Cse321, Programming Languages and Compilers
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));
11/7/2015
8
Cse321, Programming Languages and Compilers
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));
11/7/2015
9
Cse321, Programming Languages and Compilers
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
– Constant
– Exp
– Dec
11/7/2015
synthesized: (MLtype * MLtype * MLtype)
synthesized: MLtype
inherited (string*MLtype) list
synthesized: MLtype
inherited (string*MLtype) list
synthesized: ( MLtype * (string*MLtype) list)
10
Cse321, Programming Languages and Compilers
Types of programs implementing the
attribute computations
• TCOp:: OP -> (MLtype * MLtype * MLtype)
• TCConstant::
• TCExp
Constant -> MLtype
Exp ->
(string*MLtype) list ->
MLtype
• TCDec:: Dec ->
(string*MLtype) list ->
(MLtype * (string*MLtype) list)
11/7/2015
11
Cse321, Programming Languages and Compilers
Operators
fun TCOp Plus = (Int,Int,Int)
| TCOp Less = (Int,Int,Bool)
| TCOp And = (Bool,Bool,Bool);
Left argument
Result type
Right argument
11/7/2015
12
Cse321, Programming Languages and Compilers
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
11/7/2015
13
Cse321, Programming Languages and Compilers
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")
11/7/2015
14
Cse321, Programming Languages and Compilers
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
11/7/2015
15
Cse321, Programming Languages and Compilers
Infix expressions
S |- x : t1 S |- y : t2
(S <+>)= t1 * t2 -> t3
----------------------------S |- x <+> y : t3
where <+> is a binary operator like + or *
Notice how sub expressions
are type-checked first, then the
result type is computed from
those results
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
11/7/2015
16
Cse321, Programming Languages and Compilers
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
11/7/2015
17
Cse321, Programming Languages and Compilers
Function calls
S |- x : a
S |- f : a -> t
---------------------S |- f x : t
Note how the domain of
the function must have
the same type as the
actual argument.,
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")
11/7/2015 end
18
Cse321, Programming Languages and Compilers
ML statement types
S |- 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
11/7/2015
19
Cse321, Programming Languages and Compilers
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
11/7/2015
20
Cse321, Programming Languages and Compilers
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
11/7/2015
21
Cse321, Programming Languages and Compilers
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
11/7/2015
22
Cse321, Programming Languages and Compilers
ML anonymous function types
S+(x,a) |- e : b
-----------------------------S |- (fn (x:a) => e) : a -> b
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
fun TCExp x cntxt =
case x of
| Anonfun(x,t,body) =>
let val btype = TCExp body ((x,t)::cntxt)
in Arrow(t,btype) end
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
11/7/2015
23
Cse321, Programming Languages and Compilers
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)
11/7/2015
24
Cse321, Programming Languages and Compilers
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
11/7/2015
25
Cse321, Programming Languages and Compilers
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
11/7/2015
26
Cse321, Programming Languages and Compilers
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.
11/7/2015
27
Cse321, Programming Languages and Compilers
Represent types
datatype MLtype =
Unit
| Int
| Char
| Bool
| Product of MLtype list
| Arrow of (MLtype * MLtype)
| Tvar of (MLtype option) ref;
A mutable reference we can
overwrite later.
Think of the ref as a pointer (ref NONE) is a null
pointer. (ref (SOME x)) is a pointer to x
11/7/2015
28
Cse321, Programming Languages and Compilers
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 ))
11/7/2015
29
Cse321, Programming Languages and Compilers
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
11/7/2015
30
Cse321, Programming Languages and Compilers
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.
11/7/2015
31
Cse321, Programming Languages and Compilers
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
11/7/2015
32
Cse321, Programming Languages and Compilers
A Second try
fun TCExp x cntxt =
Generate a new
fresh empty slot.
case x of
I.e. guess the
| Anonfun(x, _ ,body) =>
type
let val t = Tvar(ref NONE)
val btype =
TCExp body ((x,t)::cntxt)
in Arrow(prune t,btype) end
Get rid of the indirect
reference if there is
one
11/7/2015
Hopefully typing
the body will
force the slot to
be filled in.
33
Cse321, Programming Languages and Compilers
CS321 Prog Lang & Compilers
Assigned: Feb. 28, 2006
Assignment #10
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.
11/7/2015
34