1 val toString
= Int.toString
3 (* Ported to kit
23/4/97 by Mads
.
4 Commented out module phrases
and changed a couple
of sml
/nj
things (like makestring
)
5 No rewriting to please region inference
.
6 Still it uses less that
100Kb when it
runs (see region
.ps
)
9 (* This is a quickly written
type checker for subtyping
, using the
10 MATCH
and TYPE algorithms from the Fuh
and Mishra paper
.
12 The code is ugly at places
, and no consideration has been given to
13 effeciency
-- please accept my apologies
18 structure List = (* list utilities
*)
21 type 'a set
= 'a list
; (* sets are represented by lists without repeated
26 |
isin(x
,(y
::rest
)) = x
=y
orelse isin(x
,rest
)
29 |
union((x
::rest
),l
) = if isin(x
,l
) then union(rest
,l
)
30 else x
:: union(rest
,l
);
31 fun intersect([],l
) = []
32 |
intersect((x
::rest
),l
) = if isin(x
,l
) then x
:: intersect(rest
,l
)
33 else intersect(rest
,l
);
34 fun setminus([],l
) = []
35 |
setminus(x
::l
,l
') = if isin(x
,l
') then setminus(l
,l
')
36 else x
:: setminus(l
,l
');
38 | foldr f
b (x
::rest
) = f(x
,foldr f b rest
);
40 fun foldl f base
[] = base
41 | foldl f
base (x
::xs
) = foldl
f (f(x
, base
)) xs
43 (* function for finding transitive closure
.
44 Culled from other application
.
45 DO NOT
READ (CRUDE AND INEFFICIENT
) *)
47 fun transClos (eq
: 'a
* 'a
-> bool)
48 (insert_when_eq
: bool)
49 (newsets
: 'a set list
,
50 oldsets
: 'a set list
): bool * 'a set list
=
51 (* The lists
in oldsets are pairwise disjoint
, but nothing is known about the
52 lists
in newsets
. The resulting sets are pairwise disjoint
,
53 and moreover
, the resulting boolean is
true if and only
if
54 every
member (i
.e
. list
) of newsets
, is contained
in some
member (i
.e
., list
)
57 val any
: bool = true (* could be
false, for that matter
*)
59 fun isin(x
,[]) = false
60 |
isin(x
,x
'::rest
) = eq(x
,x
') orelse isin(x
,rest
)
62 (* In the following function
, L is a list
of pairwise disjoint
63 sets
. S
' is the set currently under construction
;
64 it is also disjoint from the sets
in L
. x
65 will be added to S
' if x is not
in any set
in L
. *)
68 (found_fixpt
: bool, found_class
: bool, L
as [], S
')):
69 bool * bool * 'a list list
* 'a list
=
71 then if insert_when_eq
73 (found_fixpt
andalso found_class
, found_class
,[],x
::S
')
75 (found_fixpt
andalso found_class
, found_class
,[],S
')
77 (false, any
, [], x
::S
')
79 |
add(x
,(found_fixpt
,found_class
,(S
::L
), S
')) =
84 (found_fixpt
andalso not found_class
,true, L
, x
::(S @ S
'))
86 (found_fixpt
andalso not found_class
,true, L
, S @ S
')
88 let val (found_fixpt1
, found_class1
, L1
, S1
)=
89 add(x
,(found_fixpt
,found_class
,L
,S
'))
91 (found_fixpt
andalso found_fixpt1
,
92 found_class
orelse found_class1
,
98 fun add_S (S
,(found_fixpt
, oldsets
)) : bool * 'a set list
=
100 val (found_fixpt1
, _
, L1
, S1
) =
101 foldl
add (found_fixpt
, false, oldsets
, emptyset
) S
104 [] => (found_fixpt
andalso found_fixpt1
, L1
)
105 | _
=> (found_fixpt
andalso found_fixpt1
, S1
::L1
)
109 foldl
add_S (true, oldsets
) newsets
115 structure Variables
= (* program variables
*)
122 (*structure Type
= (* Types
, substitutions
and matching
*)
125 (* open List Variables
*)
127 *) datatype ty
= INT | REAL | ARROW
of ty
* ty | PROD
of ty
* ty | TYVAR
of int
129 type subst
= ty
-> ty
131 fun mk_subst_ty(i
: int,ty
:ty
):subst
=
135 |
S(ARROW(ty1
,ty2
)) = ARROW(S ty1
, S ty2
)
136 |
S(PROD(ty1
,ty2
)) = PROD(S ty1
, S ty2
)
137 |
S(tv
as TYVAR j
) = if i
=j
then ty
else tv
144 val r
= ref
0; (* counter for fresh
type variables
*)
145 fun fresh() = (r
:= !r
+ 1; ! r
);
146 fun fresh_ty() = TYVAR(fresh());
150 fun fv_ty (INT
,acc
) = acc
151 |
fv_ty (REAL
,acc
) = acc
152 |
fv_ty(ARROW(t1
,t2
), acc
) = fv_ty(t1
,fv_ty(t2
,acc
))
153 |
fv_ty(PROD(t1
,t2
), acc
) = fv_ty(t1
,fv_ty(t2
,acc
))
154 |
fv_ty((TYVAR i
),acc
) = if isin(i
,acc
) then acc
else i
:: acc
;
156 fun tyvars(t
:ty
) = fv_ty(t
,[])
159 foldr (fn((i
,tau
),acc
)=> fv_ty(tau
,acc
)) [] TE
161 (* ALLNEW
, page
168: create a fresh copy
of a
type, using
type variables
162 at the leaves
of the
type *)
164 fun allnew(INT
) = fresh_ty()
165 |
allnew(REAL
) = fresh_ty()
166 |
allnew(ARROW(t1
,t2
)) = ARROW(allnew(t1
),allnew(t2
))
167 |
allnew(PROD(t1
,t2
)) = PROD(allnew(t1
),allnew(t2
))
168 |
allnew(TYVAR _
) = fresh_ty();
170 (* PAIR
, page
168: create list
of atomic types that occur
in the same
171 places
of t1
and t2
*)
173 fun pair(ARROW(t1
,t2
),ARROW(t1
',t2
')) = pair(t1
,t1
') @
pair (t2
,t2
')
174 |
pair(PROD(t1
,t2
),PROD(t1
',t2
')) = pair(t1
,t1
') @
pair (t2
,t2
')
175 |
pair(t1
,t2
) = [[t1
,t2
]]
177 (* dealing
with equivalence classes
of atomic types
-- for algorithm MATCH
*)
179 (* M_v
, page
168: removing that equivalence class containing v from M
*)
181 fun remove([], v
) = []
182 |
remove((class
::rest
), v
) =
183 if isin(v
,class
) then rest
else class
:: remove(rest
,v
);
185 (* [a
]_M
, page
168: the equivalence class containing a
*)
189 fun class_of([],a
) = raise ClassOff
190 |
class_of((class
::rest
), a
) =
191 if isin(a
,class
) then class
else class_of(rest
,a
);
193 (* [t
]^M
, page
168: the set
of type variables eqivalent to some
type variable
194 occurring
in non
-atomic
type t
*)
196 fun equiv_tyvars(t
,M
): ty list
=
197 foldr union
[] ((map (fn a
=> class_of(M
,TYVAR a
)) (tyvars(t
))): ty list list
) ;
199 fun transitive_closure(oldsets
,newsets
) =
200 #
2(transClos(op = : ty
*ty
-> bool)(false)(newsets
,oldsets
))
204 type coercion
= ty
* ty
206 type coercion_set
= coercion list
208 fun on_C(S
,C
) = map (fn(t1
,t2
) => (S t1
, S t2
)) C
217 (* diagonal is used
in match to create initial equivalence relation
*)
220 fun diagonal(C
: coercion_set
) =
224 INT
=> union([INT
],acc
)
225 | REAL
=> union([REAL
],acc
)
226 |
PROD(t1
,t2
) => diag(t1
,diag(t2
,acc
))
227 |
ARROW(t1
,t2
) => diag(t1
,diag(t2
,acc
))
228 | TYVAR _
=> union([t
],acc
)
230 fun diag2((t1
,t2
),acc
) = diag(t1
,diag(t2
,acc
))
232 val atomics
= foldr diag2
[] C
234 map (fn atomic
=> [atomic
,atomic
]) atomics
237 fun ground_atomic t
=
243 fun contains_no_type_constant
[] = true
244 |
contains_no_type_constant (t
::rest
) =
245 not (ground_atomic t
) andalso contains_no_type_constant rest
;
250 fun match1([]:coercion_set
,S
:subst
,M
) = S
251 |
match1((t1
,t2
)::C
, S
, M
) =
252 if atomic t1
andalso atomic t2
then atomicElimination(t1
,t2
,C
,S
,M
)
253 else if atomic t1
then expansion(t1
,t2
,C
,S
,M
)
254 else if atomic t2
then expansion(t2
,t1
,C
,S
,M
)
255 else decomposition(t1
,t2
,C
,S
,M
)
257 and atomicElimination(t1
,t2
,C
,S
,M
) =
258 match1(C
,S
,transitive_closure(M
,[[t1
,t2
]]))
260 and decomposition(ARROW(t1
,t2
),ARROW(t1
',t2
'),C
,S
,M
)=
261 match1((t1
',t1
)::(t2
,t2
')::C
, S
, M
)
262 |
decomposition(PROD(t1
,t2
),PROD(t1
',t2
'),C
,S
,M
)=
263 match1((t1
,t1
')::(t2
,t2
')::C
, S
, M
)
264 |
decomposition(_
) = raise MATCH
266 and expansion(t1
:ty
,t2
:ty
,C
,S
,M
) =
267 case intersect(class_of(M
,t1
), equiv_tyvars(t2
,M
)) (* occurs check
*)
269 []=> if contains_no_type_constant(class_of(M
,t1
))
271 (* not matching
of int or
real with arrow or prod
*)
273 fun loop([],C
,S
,M
) = match1(C
,S
,M
)
274 |
loop((TYVAR alpha
)::tyvars
, C
, S
, M
) =
275 let val t
' = allnew t2
276 val delta
= mk_subst_ty(alpha
,t
')
278 loop(tyvars
, on_C(delta
,C
), delta
o S
,
279 transitive_closure(M
,pair(t2
,t
')))
281 |
loop((_
::tyvars
), C
, S
, M
) =
282 (* skip atomic types
, that are not variables
*)
283 loop(tyvars
, C
, S
, M
)
285 loop(class_of(M
,t1
),C
,S
,M
)
288 else (* matching
of int or
real with arrow or prod
*)
291 | _
=> (* occurs check failed
*)
295 fun match(C
) = match1(C
,Id
,diagonal C
);
299 (* Type Environments
*)
301 type tyenv
= (var
* ty
)list (* no polymorphism
! *)
303 (* looking up variables
in the
type environement
*)
307 fun lookup(i
,[]) = raise Lookup
308 |
lookup(i
,((j
,sigma
)::rest
)) = if i
=j
then sigma
else lookup(i
,rest
);
310 fun on_tyenv(S
:subst
,TE
:tyenv
) =
311 map (fn(i
,tau
) => (i
, S tau
)) TE
313 (* pretty printing
-- actually not very pretty
, but it will
do *)
315 (* precedences
: -> : 1
318 fun pp_ty
' (context
:int,INT
) = "INT"
319 | pp_ty
'(_
,REAL
) = "REAL"
320 | pp_ty
'(context
,ARROW(ty1
, ty2
)) =
321 let val s
= pp_ty
'(2,ty1
) ^
"->" ^
324 if context
> 1 then "(" ^ s ^
")" else s
326 | pp_ty
'(context
,PROD(ty1
, ty2
)) =
327 let val s
= pp_ty
'(3,ty1
) ^
"*" ^ pp_ty
'(3,ty2
)
329 if context
> 2 then "(" ^ s ^
")"
332 | pp_ty
'(context
,(TYVAR i
)) = "'a" ^ toString i
;
334 fun pp_ty ty
= pp_ty
'(0,ty
)
338 |
filter ((x
,sigma
)::rest
) =
339 if isin(x
,["fst","snd","floor"])
340 (* hack to avoid printing
of built
-in variables fst
, snd
and floor
*)
342 else (x
,sigma
)::filter rest
345 |
pp_tyenv ([(x
,sigma
)]) =
346 x ^
":" ^ pp_ty sigma ^
" "
347 |
pp_tyenv ((x
,sigma
)::rest
) =
348 x ^
":" ^ pp_ty sigma ^
"," ^ pp_tyenv rest
;
350 val pp_tyenv
= pp_tyenv
o filter
353 fun pp_coercion(tau1
,tau2
) = "\n " ^ pp_ty tau1 ^
" |> " ^ pp_ty tau2
355 fun pp_coercion_set
[] = ""
356 | pp_coercion_set
[coercion
] = pp_coercion coercion
357 |
pp_coercion_set(coercion
::rest
) =
358 pp_coercion coercion ^
", " ^ pp_coercion_set rest
365 (*structure Expressions
=
370 *) datatype exp
= VAR
of var
375 | LET
of var
* exp
* exp
380 (* pretty printing
*)
382 fun pp_exp(VAR x
) = x
383 |
pp_exp(LAM(x
,e
')) = "(fn " ^ x ^
" =>" ^ pp_exp e
' ^
")"
384 |
pp_exp(APP(e1
,e2
)) = "(" ^ pp_exp e1 ^
" @ " ^ pp_exp e2 ^
")"
385 |
pp_exp(LET(x
,e1
,e2
)) = "let " ^ x ^
"=" ^ pp_exp e1 ^
" in " ^ pp_exp e2 ^
"end"
386 |
pp_exp(INTCON i
) = toString i
387 |
pp_exp(REALCON r
) = toString(floor r
) ^
".?"
388 |
pp_exp(PLUS(e1
)) = "(+" ^ pp_exp e1 ^
")"
389 |
pp_exp(MINUS(e1
)) = "(-" ^ pp_exp e1 ^
")"
390 |
pp_exp(PAIR(e1
,e2
)) = "(" ^ pp_exp e1 ^
"," ^ pp_exp e2 ^
")"
397 (*structure TypeChecker
=
400 open List Variables Type Expressions
403 type job
= tyenv
* exp
* ty
405 fun TYPE(TE
:tyenv
,e
:exp
) : ty
* coercion_set
=
406 let val alpha0
= fresh_ty()
407 val C
= TY([(TE
,e
,alpha0
)],[])
411 and TY([]: job list
, C
: coercion_set
)= C
412 |
TY((job
as (TE
,e
,tau
)) :: rest
, C
: coercion_set
)=
414 VAR x
=> TY(rest
, (lookup(x
,TE
),tau
)::C
)
416 let val (new_ty1
,new_ty2
) = (fresh_ty(),fresh_ty());
417 val TE
' = (x
,(new_ty1
))::TE
419 TY((TE
',e
',new_ty2
)::rest
, (ARROW(new_ty1
,new_ty2
), tau
):: C
)
422 let val (new_ty1
,new_ty2
) = (fresh_ty(),fresh_ty());
424 TY((TE
,e1
,ARROW(new_ty1
,new_ty2
))::(TE
,e2
,new_ty1
)::rest
,
428 let val (new_ty1
,new_ty2
) = (fresh_ty(),fresh_ty());
430 TY((TE
,e1
,new_ty1
)::((x
,new_ty1
)::TE
,e2
,new_ty2
)::rest
,
433 | INTCON i
=> TY(rest
, (INT
, tau
):: C
)
434 | REALCON i
=> TY(rest
, (REAL
, tau
):: C
)
436 | PLUS e1
=> TY((TE
,e1
, PROD(INT
,INT
))::rest
, (PROD(INT
,INT
),tau
):: C
)
437 | MINUS e1
=> TY((TE
,e1
, PROD(INT
,INT
))::rest
, (PROD(INT
,INT
),tau
):: C
)
439 let val (new_ty1
,new_ty2
) = (fresh_ty(),fresh_ty());
441 TY((TE
,e1
,new_ty1
)::(TE
,e2
,new_ty2
)::rest
,
442 (PROD(new_ty1
,new_ty2
),tau
)::C
)
445 type judgement
= coercion_set
* tyenv
* exp
* ty
447 fun pp_judgement(C
,TE
,e
,tau
) =
448 "\nCoersion set: " ^ pp_coercion_set C ^
449 "\nType environment: " ^ pp_tyenv TE ^
450 "\nExpression: " ^ pp_exp e ^
451 "\nType: " ^ pp_ty tau
;
462 open List Variables Type Expressions TypeChecker
464 *) val e0
= (* let val Id
= fn x
=> x
in (Id
3, Id
4.0) end *)
466 val Id
= LAM("x",VAR
"x")
467 val pair
= PAIR(APP(VAR
"Id", INTCON
3), APP(VAR
"Id", REALCON
4.0))
472 val e1
= (* let val makepair
= fn x
=> (x
,x
) in makepair
3.14 *)
474 val makepair
= LAM("x",PAIR(VAR
"x", VAR
"x"))
476 LET("makepair", makepair
, APP(VAR
"makepair", REALCON
3.14))
479 val e2
= (* let fun mappair
= fn f
=> fn x
=> (f (fst x
), f (snd x
))
480 in mappair
floor (3.14,2.18) end *)
483 val mappair
= LAM("f",LAM("x",
484 PAIR(APP(VAR
"f",APP(VAR
"fst", VAR
"x")),
485 APP(VAR
"f",APP(VAR
"snd", VAR
"x")))))
487 LET("mappair",mappair
, APP(APP(VAR
"mappair", VAR
"floor"),
488 PAIR(REALCON
3.14, REALCON
2.18)))
491 val e3
= (* fn x
=> x
*)
493 val e4
= (* fn f
=> fn x
=> f x
*)
494 LAM("f",LAM("x",APP(VAR
"f", VAR
"x")));
495 val e5
= (* fn f
=> fn x
=> f(f x
) *)
496 LAM("f",LAM("x",APP(VAR
"f", APP(VAR
"f", VAR
"x"))));
499 fun run(e
: exp
, outfilename
: string) =
501 val _
= r
:= 5; (* reset counter for
type variables
*)
502 val _
= print("\nprocessing " ^ outfilename ^
"...")
504 [("fst", (ARROW(PROD(TYVAR
0,TYVAR
1),TYVAR
0))), (* the
type of fst
*)
505 ("snd", (ARROW(PROD(TYVAR
0,TYVAR
1),TYVAR
1))), (* the
type of snd
*)
506 ("floor", (ARROW(REAL
,INT
)))] (* the
type of floor
*)
507 val _
= print("running TYPE...")
508 val (ty
,C
) = TYPE(TE0
,e
) ;
509 val os
= TextIO.openOut outfilename
;
510 val _
= TextIO.output(os
, "\nResult of TYPE:\n\n\n " ^
pp_judgement(C
,TE0
,e
,ty
));
511 val _
= print("running MATCH...")
513 val judgem
' as (C
',TE
',e
',ty
') = (on_C(S
,C
),on_tyenv(S
,TE0
),e
,S ty
)
514 val _
= TextIO.output(os
, "\n\n\nResult of MATCH:\n\n\n " ^ pp_judgement judgem
');
519 val _
= run(e0
,"outFuhMishra0");
520 val _
= run(e1
,"outFuhMishra1");
521 val _
= run(e2
,"outFuhMishra2");
522 val _
= run(e3
,"outFuhMishra3");
523 val _
= run(e4
,"outFuhMishra4");
524 val _
= run(e5
,"outFuhMishra5");