+ All Categories
Home > Documents > 1 Dependent Types in Practical Programming Hongwei Xi University of Cincinnati.

1 Dependent Types in Practical Programming Hongwei Xi University of Cincinnati.

Date post: 21-Dec-2015
Category:
View: 217 times
Download: 1 times
Share this document with a friend
Popular Tags:
48
1 Dependent Types in Practical Programming Hongwei Xi University of Cincinnati
Transcript

1

Dependent Types inPractical Programming

Hongwei XiUniversity of Cincinnati

2

Overview

Motivation Program error detection at compile-time Compilation certification Proof carrying code (PCC)

Dependently typed programming languages Design decisions Dependent ML (functional) and Xanadu (imperative) Theoretical development Practical applications

ConclusionDemo

3

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

Our work in this direction is inspired by and closely related to the work on refinement types (Davies, Freeman and Pfenning)

4

Some Advantages of Types

Detecting program errors at compile-timeEnabling compiler optimizationsFacilitating program verification Using types to encode program properties Verifying the encoded properties through

type-checking

Serving as program documentation Unlike informal comments, types are formally

verified and can thus be fully trusted

5

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

6

Narrowing the Gap

NuPrl Coq

Program Extraction Proof Synthesis

ML

Dependent ML

7

Safe Array Subscripting

int(n): the singleton type for expressions of value n, where n ranges over integers‘a array(n): the type for arrays of size n, where n ranges over natural numberslength: {n:nat} ‘a array(n) -> int(n)

sub: {i:nat,n:nat | i < n} ‘a array(n) * int(i) -> ‘a

update: {i:nat,n:nat | i < n} ‘a array(n) * int(i) * ‘a -> unit

8

Dot Product in DML

fun dotprod (u, v) =let fun loop (i, len, sum) = if i = len then sum else loop (i+1, len, sum + sub(u,i) * sub(v,i))

in loop (0, length (u), 0)end

withtype {i:nat | i <= n} int (i) * int(n) * int -> int

withtype {n:nat} int array(n) * int array(n) -> int

9

A polymorphic type for arrays record <‘a> array { size: int; data[]: ‘a }But this does not enforce that the integer stored in size is the size of the array to which data points

size data

A Type for Arrays

10

A Dependent Type for Arrays

A polymorphic type for arrays

{n:nat}record <‘a> array(n) { size: int(n); data[n]: ‘a}

11

Dot Product in Xanadu

int dp (u: <int>array , v: <int>array ) { var: int i, sum;; sum = 0;

for (i=0; i < u.size; i = i+1) { sum = sum + u.data[i] * v.data[i]; } return sum;

}

{n:nat}

invariant: [a:int | a >= 0] (i: int(a))

(n) (n)

12

Some Design Decisions

Practical type-checkingRealistic programming featuresConservative extensionPay-only-if-you-use policy

13

ML0: start point

base types ::= int | bool | (user defined datatypes)

types ::= | | 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:. e | fix f:. e | e1(e2) | let x=e1 in e2 end | case e of ms

values v ::= x | c | <v1, v2> | lam x:. econtext ::= . | , x:

14

Integer Constraint Domain

We use a for index variablesindex 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 | P Q | P Q

index sorts ::= int | {a : | P }index variable contexts ::= . | , a: | , Pindex constraints ::= P | P | a:

15

Dependent Types

dependent types ::= ... | (i) | a: . | a: .

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

DML0ML0 + dependent types

expressionse ::= ... | a: .v | e[i] | <i e> | open e1 as <a x> in e2 end

values v ::= ... | a: .v | <i v>typing judgment e

17

Some Typing Rules

aetype-ilamaea

aeaitype-iappeiai

18

Some Typing Rules (cont’d)

eaiitype-pack<i e>a

e1aaxe2type-openopen e1 as <a x> in e2 end:

19

Some Typing Rules (cont’d)

ebool(i)ie1: ie2: type-ifif (e, e1, e2):

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

answer:type in DML0

program:type in DML0

|program|:|type| in ML0 |answer|:|type| in ML0

evaluation

evaluation

erasure erasure

Type preservation holds in DML0

A program is already typable in ML0 if it is typable in DML0

22

Elaboration for DML0

Elaboration is a mapping that maps an external language into an internal language We have constructed an algorithm

doing elaboration for DML0

We have proven the correctness of the algorithm

23

An Example of Elaboration

fun zip ([], []) = []| zip (x :: xs, y :: ys) = (x, y) :: zip (xs, ys)

withtype {n:nat} ‘a list(n) * ‘b list(n) -> (‘a * ‘b) list(n)

fun zip[0] ([], []) = [] | zip[a+1] (cons[a] (x, xs), cons[a] (y, ys)) = cons[a] ((x, y), zip[a] (xs, ys))withtype {n:nat} ‘a list(n) * ‘b list(n) -> (‘a * ‘b) list(n)

24

A Sample Constraint

The following constraint is generated during type-checking the zip function:

p:nat. q:nat. p + 1 = n q + 1 = n p = q

25

A Use of Existential Types

fun filter p [] = [] | filter p (x :: xs) = if p(x) then x :: (filter p xs) else filter p xs

withtype(‘a -> bool) ->{n:nat} ‘a list(n) ->[m:nat | m <= n] ‘a

list(m)‘a list (* [m:nat] ‘a list(m) *)

26

Polymorphism

Polymorphism is largely orthogonal to dependent typesWe have adopted a two phase type-checking algorithm

27

References and Exceptions

A straightforward combination of effects with dependent types leads to unsoundnessWe have adopted a form of value restriction to restore the soundness

28

Quicksort in DML

fun qs [] = []| qs (x :: xs) = par (x, xs, [], [])

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 {n:nat} int list(n) -> int list(n)

withtype {p:nat,q:nat,r:nat} int * int list(p) * int list(q) * int list(r) -> int list(p+q+r+1)

29

Quicksort in DML (cont’d)

Note that qs(xs) is a permutation of xsdatatype intlist with (nat, nat) =

Nil(0,0)| {i:int,s:int,l:nat} Cons(s+i,l+1) of int(i) * intlist(s,l)

Nil: intlist(0,0)Cons: {i:int,s:int,l:nat}

int(i) * intlist(s,l) -> intlist(s+i,l+1)qs: {s:int,l:nat} intlist(s,l) -> intlist(s,l)

30

Binary Search in Xanadu

int bs(key: int, vec: <int> array ) { var: int l, m, u, x;; l = 0; u = vec.size - 1;

while (l <= u) { m = l + (u-l) / 2; x = vec.data[m]; if (x < key) { l = m + 1; } else if (x > key) { u = m - 1; } else { return m; } } return –1;}

invariant: [i:int,j:int | 0<=i<=j+1<=n] (l:int(i), u:int(j))

{n:nat}(n)

31

Termination Verification

Termination is a liveness property can not be verified at run-time is often proven with a well-founded

metric that decreases whenever a recursive function call is made

32

Ackermann Function in DML

fun ack (m, n) =if m = 0 then n+1else if n = 0 then ack (m-1, 1) else ack (m-1, ack (m, n-1))

withtype{m:nat,n:nat} int(m) * int(n) -> int

<m,n> =>

33

Metric Typing Judgments

Definition (Metric) Let = <i1,...,in> be a tuple of index expressions. We write : metric if we have ij:nat for 1 j n.

We use a for a decorated type

We use the judgemntefto mean that for each occurrence of f[i] in e, [a->i] holds, where f is declared in to have type a

34

Some Metric Typing Rules

The rule (app) is: e1fe2f

e1(e2): f

The rule ( lab) is: iaifa

fiaif

35

DML0,

The following typing rule is for forming functions:

afa |- e: ftype-fun)

fun fais e: a

36

Reducibility

Definition Suppose that e is a closed expression of type and e * v holds for some value v. is a base type. Then e is reducible Then e is reducible if e(v1) is

reducible for every reducible value v1 of type .

Then e is reducible if v=<v1, v2> and v1, v2 are reducible.

a: . Then e is reducible if e[i] is reducible for every i:.

a: . Then e is reducible if v=<i | v1> and v1 is reducible.

37

-reducibility

Definition Let e be a well-typed closed function fun f[a:]: is v and be a closed metric. e is-reducible if e[i] is reducible for each i satisfying [a->i] .

Theorem Every closed expression e is reducible if it is well-typed in DML0,

38

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)

<n,0> =>

<p+q+r,p+1> =>

39

Ongoing Research

Compilation certificationDependently typed assembly language (with Robert Harper)Proof construction for proof-carrying code

40

Compiler Correctness

How can we prove the correctness of a (realistic) compiler?

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)

Source program

e

Target code

|e|

compilation

| . |

41

Semantics-preserving Compilation

e -------------> |e|

D of ev ----> |D| of |e||v|

This seems unlikely to be feasible in practice

42

Compilation Certification

Assume that eholds, i.e., e has the property Then eshould hold, tooA compiler can be designed to produce a certificate to assert that edoes have the property

Target code

e: e holds

Source program

e: e holds

compilation

| . |

43

Type-preserving Compilation

e --------------> |e| e: -----------> |e|:||

D of e: ----> |D| of |e|:|| D and |D| are both represented in LF The LF type-checker does all type-checking!

44

Proof-Carrying Code

Proof

Poof-CarryingCode

Unpacking

Verifying

Executing

Memory SafetyTermination

Code

45

Proof Construction

Building type derivations at source level with a practical type inference algorithmTranslating such type derivations into proofs at target level

Target code

eSource program

e

compilation

| . |

proof

translationProof of e Proof of e

46

Contributions

Novel language designReduction of type-checking to constraint satisfactionUnobtrusive programming via elaborationSolid theoretical foundationPrototype implementation and evaluation

47

Related Work

Refinement types (Freeman, Davis & Pfenning)Cayenne (Augustsson)TALC Compiler (Morrisett et al at Cornell)Safe C compiler: Touchstone (Necula & Lee)TIL compiler (the Fox project at CMU)FLINT compiler (Shao et al at Yale)Secure Internet Programming (Appel, Felten et al at Princeton)

48

End of the Talk (Demo Time)

Thank You! Questions?


Recommended