CPS Transform for Dependent ML Hongwei Xi University of Cincinnati and Carsten Schürmann Yale University Overview Motivation Program error detection at compile-time Compilation certification Dependent ML (DML) Programming Examples Theoretical Foundation CPS Transform.
Download ReportTranscript CPS Transform for Dependent ML Hongwei Xi University of Cincinnati and Carsten Schürmann Yale University Overview Motivation Program error detection at compile-time Compilation certification Dependent ML (DML) Programming Examples Theoretical Foundation CPS Transform.
CPS Transform for
Dependent ML
Hongwei Xi
University of Cincinnati
and
Carsten Schürmann
Yale University
1
Overview
Motivation
Program error detection at compile-time
Compilation certification
Dependent ML (DML)
Programming Examples
Theoretical Foundation
CPS Transform for DML
Conclusion
2
Program Error Detection
Unfortunately one often pays a price for [languages which
impose no disciplines of types] in the time taken to find
rather inscrutable bugs — anyone who mistakenly applies
CDR to an atom in LISP and finds himself absurdly adding a
property list to an integer, will know the symptoms.
-- Robin Milner
A Theory of Type Polymorphism in Programming
Therefore, a stronger type discipline allows for capturing
more program errors at compile-time.
3
Some Advantages of Types
Detecting program errors at compile-time
Enabling compiler optimizations
Facilitating program verification
Using types to encode program properties
Verifying the encoded properties through typechecking
Serving as program documentation
Unlike informal comments, types are formally
verified and can thus be fully trusted
4
Compiler Correctness
How can we prove the correctness of
a (realistic) compiler?
Source program
e
compilation
Target code
|.|
|e|
Verifying that the semantics of e is the same
as the semantics of |e| for every program e
But this simply seems too challenging (and is
unlikely to be feasible)
5
Compilation Certification
Source program
e: P(e) holds
compilation
|.|
Target code
|e|: P(|e|) holds
Assume that P(e) holds, i.e., e has the
property P (e.g., memory safety,
termination, etc.)
Then P(|e|) should hold, too
A compiler can be designed to produce a
certificate to assert that |e| does have the
property P
6
Semantics-preserving Compilation
e -------------> |e|
D of ev --> |D| of |e||v|
This seems unlikely to be feasible in
practice
7
Type-preserving Compilation
e --------------> |e|
e:t -----------> |e|:|t|
D of e:t ----> |D| of |e|:|t|
D and |D| are both represented in LF
The LF type-checker does all type-checking!
8
Limitations of (Simple) Types
Not general enough
Many correct programs cannot be typed
For instance, downcasts are widely used
in Java
Not specific enough
Many interesting program properties
cannot be captured
For instance, types in Java cannot
guarantee safe array access
9
Narrowing the Gap
Coq
NuPrl
Program Extraction
Proof Synthesis
Dependent ML
ML
10
Some Design Decisions
Practical type-checking
Realistic programming features
Conservative extension
Pay-only-if-you-use policy
11
Ackermann Function in DML
fun ack (m, n) =
if m = 0 then n+1
else if n = 0 then ack (m-1, 1)
else ack (m-1, ack (m, n-1))
withtype
{a:nat,b:nat} int(a) * int(b) -> nat
(* Note: nat = [a:int | a >=0] int(a) *)
12
Binary Search in DML
fun bs (vec, key) =
let
fun loop (l, u) =
if l > u then –1
else let
val m = (l + u) / 2
val x = sub (vec, m) (* m needs to be within bounds *)
in
if x = key then m
else if x < key then loop (m+1, u)
else loop (l, m-1)
end
withtype {i:int,j:int | 0 <= i <= j+1 <= n} int(i) * int(j) -> int
in loop (0, length (vec) – 1) end
withtype {n:nat} ‘a array(n) * ‘a -> int
(* length: {n:nat} ‘a array(n) -> int(n) *)
(* sub: {n:nat,i:nat | i < n} ‘a array(n) * int(i) -> ‘a *)
13
ML0: start point
base types
d ::= int | bool | (user defined datatypes)
types t ::= d | t1 t2 | t1 * t2
patterns p ::= x | c(p) | <> | <p1, p2>
match clauses ms ::= (p e) | (p e | ms)
expressions
e ::= x | f | c | if (e, e1, e2) | <> | <e1, e2> |
lam x:t. e | fix f:t. e | e1(e2) |
let x=e1 in e2 end | case e of ms
values v ::= x | c | <v1, v2> | lam x:t. e
context G ::= . | G, x: t | G, f: t
14
Integer Constraint Domain
We use a for index variables
index expressions
i, j ::= a | c | i + j | i – j | i * j | i / j | …
index propositions
P, Q ::= i < j | i <= j | i > j | i >= j | i = j | i <> j |
PQ|PQ
index sorts g ::= int | {a : g | P }
index variable contexts f ::= . | f, a: g | f, P
index constraints F ::= P | P F | a: g. F
15
Dependent Types
dependent types
t ::= ... | d(i) | Pa: g. t | Sa: g. t
For instance,
int(0), bool array(16);
nat = [a:int | a >= 0] int(a);
{a:int | a >= 0} int list(a) -> int list(a)
16
DML0: ML0 + dependent types
expressions
e ::= ... | la: g.v | e[i] |
<i | e> | open e1 as <a | x> in e2 end
values v ::= ... | la: g.v | <i | v>
typing judgment f; G |- e: t
17
Some Typing Rules
f, a:g; G |- e: t
------------------(type-ilam)
f; G |- la:g.e: Pa:g.t
f; G |- la:g.e: Pa:g.t f |- i: g
------------------------(type-iapp)
f; G |- e[i]: t[a:=i]
18
Some Typing Rules (cont’d)
f; G |- e: t[a:=i]
f |- i: g
-------------------------(type-pack)
f; G |- <i | e>: Sa:g.t
f; G |- e1: Sa:g.t1
f, a:g; G, x:t1 |- e2: t2
---------------------------------(type-open)
f; G |- open e1 as <a | x> in e2 end: t2
19
Some Typing Rules (cont’d)
f; G |- e: bool(i)
f,i=1; G |- e1: t f,i=0; G |- e2: t
------------------------(type-if)
f; G |- if (e, e1, e2): t
20
Erasure: from DML0 to ML0
The erasure function erases all syntax
related to type index
| bool(1) | = |bool(0)| = bool
| [a:int | a >= 0] int(a) | = int
| {n:nat} ‘a list(n) -> ‘a list(n) | =
‘a list -> ‘a list
| open e1 as <a | x> in e2 end | =
let x = |e1| in |e2| end
21
Relating DML0 to ML0
program:type in DML0
evaluation
erasure
|program|:|type| in ML0
answer:type in DML0
erasure
evaluation
|answer|:|type| in ML0
Type preservation holds in DML0
A program is already typable in ML0 if it is typable in DML0
22
Polymorphism
Polymorphism is largely orthogonal to
dependent types
We have adopted a two phase typechecking algorithm
23
References and Exceptions
A straightforward combination of
effects with dependent types leads
to unsoundness
We have adopted a form of value
restriction to restore the soundness
24
Quicksort in DML
fun qs [] = []
| qs (x :: xs) = par (x, xs, [], [])
withtype {n:nat} int list(n) -> int list(n)
and par (x, [], l, g) = qs (l) @ (x :: qs (g))
| par (x, y :: ys, l, g) =
if y <= x then par (x, ys, y :: l, g)
else par (x, ys, l, y :: g)
withtype
{p:nat,q:nat,r:nat}
int * int list(p) * int list(q) * int list(r) ->
int list(p+q+r+1)
25
CPS transformation for DML (I)
Transformation on types:
|| d(i) ||* = d(i)
|| t1 -> t2 || = || t1 ||* -> || t2 ||
||P a:g. t||* = P a:g. ||t||*
||S a:g. t||* = S a:g. ||t||*
||t|| = ||t||* -> ans -> ans
(* ans is some newly introduced type *)
26
CPS Transformation for DML (II)
Transformation on expressions:
||c||* = c ||x||* = x
||le x.e||* = le x. ||e||
||v|| = le k. k(||v||*)
||e1(e2)|| = le k.||e1||(le x1.||e2||(le x2 . x1 (x2)(k))
||e[i]|| = le k.||e||(le x . x[i](k))
||fix f.v|| = le k.(fix f. ||v||)(k)
... ...
27
CPS Transformation for DML
Theorem
Assume D :: f; G |- e : t. Then D can be
transformed to ||D|| such that
||D|| :: f; ||G|| |- ||e|| : ||t||,
where ||G||(x) = ||G(x)||* and ||G||(f) =
||G(f)|| for all x,f in the domain of G .
This theorem can be readily encoded into LF
We have done this in Twelf.
28
Contributions
A CPS transform for DML
The transform can be lifted to the level
of typing derivation
The notion of typing derivation
compilation
A novel approach to compilation
certification in the presence of
dependent types
29
End of the Talk
Thank You!
Questions?
30