360 likes | 422 Views
Explore the application of dependent types in imperative programming, focusing on error detection and proof generation. Learn about the design decisions, type system, limitations, and advantages of dependent types with programming examples.
E N D
Imperative Programming with Dependent Types Hongwei Xi Boston University
Talk Overview • Motivation • Detecting program errors (at compile-time) • Generating proof-carrying code • Programming language Xanadu: • Design decisions • Dependent type system • Programming examples • Current Status and Future Work
A Wish List • We would like to have a programming language that should • be simple and general • support extensive error checking • facilitate proofs of program properties • possess correct and efficient implementation • ... ...
Reality • Invariably, there are many conflicts among this wish list • These conflicts must be resolved with careful attention to the needs of the user
Advantages of Types • Capturing errors at compile-time • Enabling compiler optimizations • Facilitating program verification • Using types to encode program properties and verifying the encoded properties through type-checking • Serving as program documentation • Unlike informal comments, types can be fully trusted after type-checking
Limitations of (Simple) Types • Not general enough • Many correct programs cannot be typed • For instance, type casts are widely used in C • Not specific enough • Many interesting properties cannot be captured • For instance, types in Java cannot handle safe array access
Narrowing the Gap NuPrl Coq Program Extraction Proof synthesis ML with Dependent Types ML
Informal Program Comments /* the function should not be applied to a negative integer */ int factorial (x: int) { if (x < 0) exit(1); /*defensive programming*/ if (x == 0) return 1;else return (x * factorial (x-1)); }
Formalizing Program Comments {n:nat} int factorial (x: int(n)) { if (x == 0) return 1;else return (x * factorial (x-1)); } Note: factorial (-1) is ill-typed and rejected!
Informal Program Comments /* arrays a and b are of equal size */ float dotprod (float a[], float b[]) { int i; float sum = 0.0; if (a.size != b.size) exit(1); for (i = 0; i < a.size; i = i + 1) { sum = sum + a[i] * b[i]; } return sum; }
Formalizing Program Comments {n:nat} float dotprod (a: <float> array(n), b: <float> array(n)) { /* dotprod is assigned the following type: {n:nat}. (<float> array(n), <float> array(n)) -> float */ /* function body */ … … … }
Dependent Types • Dependent types are types that are • more refined • dependent on the values of expressions • Examples • int(i): singleton type containing only integer i • <int> array(n): type for integer arrays of size n
Type System Design • A practically useful type system should be • Scalable • Applicable • Comprehensible • Unobtrusive • Flexible
Xanadu • Xanadu is a dependently typed imperative programming language with C-like syntax • The type of a variable in Xanadu can change during execution • The programmer may need to provide dependent type annotations for type-checking purpose
Early Design Decisions • Practical type-checking • Realistic programming features • Conservative extension • Pay-only-if-you-use policy
Examples of Dependent Types (I) • int(a): singleton types containing the only integer equal to a, where a ranges over all integers • <‘a> array(a): types for arrays of size a in which all elements are of type ‘a, where ‘a ranges over all natural numbers
Examples of Dependent Types (II) • int(i,j) is defined as [a:int | i < a < j] int(a),that is, the sum of all types int(a) for i < a < j • int[i,j), int(i,j] , int[i,j] are defined similarly • nat is defined as [a:int | a >=0] int(a)
A Xanadu Program {n:nat} unit init (int vec[n]) { var: int ind, size;; /* arraysize: {n:nat} <‘a> array(n) -> int(n) */ size = arraysize(vec); invariant: [i:nat] (ind: int(i)) for (ind=0; ind<size; ind=ind+1){ vec[ind] = ind; } }
A Slight Variation {n:nat} unit init (int vec[n]) { var: nat ind, size;; /* arraysize: {n:nat} <‘a> array(n)-> int(n) */ size = arraysize(vec); for (ind=0; ind<size; ind=ind+1){ vec[ind] = ind; } }
Dependent Record Types • A polymorphic type for arrays{n:nat} <‘a> array(n) { size: int(n); data[n]: ‘a}
Binary Search in Xanadu {n:nat} int bs(key: int, vec: <int> array(n)) { var: l: int [0, n], h: int [-1, n); int m, x;; l = 0; h = vec.size - 1; while (l <= h) { m = (l + h) / 2; x = vec.data[m]; if (x < key) { l = m - 1; } else if (x > key) { h = m + 1; } else { return m; } } return –1; }
Dependent Record Types • A polymorphic type for 2-dimensional arrays:{m:nat,n:nat} <‘a> array2(m,n) { row: int(m); col: int(n); data[m][n]: ‘a}
Dependent Record Types • A polymorphic type for sparse arrays: {m:nat,n:nat}<‘a>sparseArray(m,n) { row: int(m); col: int(n); data[m]: <int[0,n) * ‘a> list}
Dependent Union Types • A polymorphic type for lists:union <‘a> list with nat = { Nil(0); {n:nat} Cons(n+1) of ‘a * <‘a> list(n) } • Nil: <‘a> list(0) • Cons: {n:nat}‘a * <‘a> list(n)-> <‘a> list(n+1)
Dependent Union Types • A polymorphic type for binary trees:union <‘a> tree with (nat,nat) = { E(0,0); {sl:nat,sr:nat,hl:nat,hr:nat} B(sl+sr+1,1+max(hl,hr)) of <‘a> tree(sl,hl) * ‘a * <‘a> tree(sr,hr) }
Typing Judgment in Xanadu f1;D1;G |- e: (f2;D2;t)f: Context for index variables • D: Context for variables with mutable types • G: Context for variables with fixed types
Typing Assignment • f1;D1;G |- e: (f2;D2;t)---------------------f1;D1;G |- x = e: (f2;D2[x->t];unit)
Typing Loop f1;D1 |- $f.D f1,f;D;G|- e1:(f2;D2;bool(i)) f2,i=1;D2;G |- e2:(f3;D3;unit) f3;D3 |- $f.D----------------------------f1;D1;G |-while(e1,e2):(f2,i=0;D2;unit)
Reverse Append in Xanadu (‘a) {m:nat,n:nat} <‘a> list(m+n) revApp (xs:<‘a> list(m),ys:<‘a> list(n)) {var: ‘a x;;invariant: [m1:nat,n1:nat | m1+n1=m+n] (xs:<‘a> list(m1), ys:<‘a> list(n1))while (true) { switch (xs) { case Nil: return ys; case Cons (x, xs): ys = Cons(x, ys); } } exit; /* can never be reached */ }
Constraint Generation in Type-checking The following integer constraint is generated when the revApp example is type-checked: m:nat,n:nat, m1:nat,n1:nat, m1+n1=m+n, a:nat, m1=a+1 |= a+(n1+1)=m+n
Current Status of Xanadu • A prototype implementation of Xanadu in Objective Caml that • performs two-phase type-checking, and • generates assembly level code • An interpreter for interpreting assembly level code • A variety of examples athttp://www.cs.bu.edu/~hwxi/Xanadu/Xanadu.html
Conclusion (I) • It is still largely an elusive goal in practice to verify the correctness of a program • It is therefore important to identify those program properties that can be effectively verified for realistic programs
Conclusion (II) • We have designed a type-theoretic approach to capturing simple arithmetic reasoning • The preliminary studies indicate that this approach allows the programmer to capture many more properties in realistic programs while retaining practical type-checking
Future Work • Adding more programming features into Xanadu • in particular, OO features • Type-preserving compilation: constructing a compiler for Xanadu that can translate dependent types from source level into bytecode level • Incorporating dependent types into (a subset of) Java and …
Related Work • Here is a (partial) list of some closely related work. • Dependent types in practical programming (Xi & Pfenning) • TALC Compiler (Morrisett et al at Cornell) • Safe C compiler (Necula & Lee) • TIL compiler (the Fox project at CMU)