The Marriage of Dependent Types and
EffectsGreg Morrisett
Joint with A.Nanevski (MSR), L.Birkedal (ITU), R.Peterson (ITU), P.Govereau, R.Wisnesky
QuickTime™ and aTIFF (Uncompressed) decompressor
are needed to see this picture.
QuickTime™ and aTIFF (Uncompressed) decompressor
are needed to see this picture.
The Marriage of Dependent Types and
Effects
QuickTime™ and aTIFF (Uncompressed) decompressor
are needed to see this picture.
QuickTime™ and aTIFF (Uncompressed) decompressor
are needed to see this picture.
Greg Morrissette
Joint with A.Nanevski (MSR), L.Birkedal (ITU), R.Peterson (ITU), P.Govereau, R.Wisnesky
27 June 2007 3
From SML to ZML?A discussion at the last ML workshop:
Things people generally agreed upon:– better support for refinement (and thus dependent) types– better treatment of effects– better support for concurrency
Code name: YMLThis talk : Y0 (a.k.a. Y-naught, Y-not, Y-knot, …)
Next year : Y-succ
27 June 2007 4
Arrays/Vectors
An embarrassment to ML:
sub : array int
So a buffer can cause your server to crash with an uncaught exception.– either refine domain of sub,– or at least reflect when it throws
27 June 2007 5
What we desire:sub : x:array , {i:int | 0 <= i < size x}
csub : x:array ,i:int, throws IndexOutOfBounds
when i < 0 or i > size x
But this raises a lot of subtle questions:• how to introduce and eliminate refinements?• what is the language of refinements?• how to rectify effects with dependency?• …
27 June 2007 6
What does this mean?sub : x:array , {i:int | 0 <= i < size x} ->
let x : array int = #[0,1,2,3 ] i : ref int = ref 3in sub x (i := rand() mod 3; fork g; !i)end
27 June 2007 7
DML [Xi & Pfenning]• Add modeling types at the kind level:
– ::= * | Nat | Bool – ::= 0 | Succ | NatRec | …
• Connect type and term levels with singletons – 0 : S(0), succ : n:Nat, S(n) S(Succ n)
• Limited predicates on modeling types:– sub : n,m:Nat [nm]array n S(m)
• Many follow-ons – ATS, Omega, Concoqtion, …
27 June 2007 8
DML-style dependency
Pros:– conservative over ML– avoids issues with effects– strong phase separation
Cons:– in practice, lots of duplication
• not just in meta-theory, but in the code.
– can’t say anything about computations– can’t say much about state, IO, exns, …
27 June 2007 9
An Alternative: Y0
Started with type theory.– we’re doing a shallow embedding in Coq.– (but other environments might do as well)– keys: support for dependency, refinement, etc.– and extraction to ML or Haskell
Add a distinct type of effectful computations.– monadic types as in Haskell: ◊A– but indexed with predicates: ◊{P}A{Q}.– includes recursion, (h.o.) state, exns, …– lots of attention to frame, modularity conditions.
More or less “pay-as-you-go.”
27 June 2007 10
A Very Simple Example:Definition postinc x := xv <-- !!x ; x ::= (xv + 1) ;; ret xv.
You can extract the corresponding Haskell:postinc x = do {xv <- read x ; write x (xv + 1) ; return xv }
27 June 2007 11
Expanding the DefinitionDefinition postinc x := xv <-- !!x ; x ::= (xv + 1) ;; ret xv.
Expands into: Definition postinc x := bind (read x) ( xv => bind (write x (xv + 1)) ( _ => ret xv)).
27 June 2007 12
The ST Monad in Coq
ST(P:pre)(A:Set)(Q:post A) : Set
Read as Hoare-partial correctness:– a computation which– when run in a heap satisfying P,– diverges or else produces a value of type
A and a heap satisfying Q.
27 June 2007 13
Roadmap
ST(P:pre)(A:Set)(Q:post A) : Set
• Heaps, pre- & post-conditions• Basic monadic & state constructs• Exceptions, conditionals, arrays• Example: hashtable• Recursion and meta-theoretic issues• Modularity & separation• Current & future work
27 June 2007 14
Heaps, Pre & Post-Conditions
ST(P:pre)(A:Set)(Q:post A) : Set.
pre := heap->Prop
post(A:Set) := A->heap->heap->Prop.
loc : Set.
heap := loc->option Dynamic.
Dynamic := {A:Set ; v:A }.
27 June 2007 15
Some Primitives & Types:top := fun (h:heap) => True.
(* lift a pure term x into the computation space *)
ret : (x:A),ST top A ( r i f => r=x f=i).
(* allocate a fresh location *)
new : (v:A),ST top loc ( r i f => i r = None f = update r v i).
Note: leaving off implicit parameters.
27 June 2007 16
More Primitivesptsto(x:loc)(v:A)(h:heap) := h x = Some{A; v}ref(A:Set)(x:loc)(h:heap):= v:A,ptsto x v h.
read : (A:Set)(x:loc), ST (ref A x) A
( r i f => f = i ptsto x r i)
write : (x:loc)(v:A), ST ( i => B, ref B x i) unit ( r i f => f = update x v i)
27 June 2007 17
Bind(* sequentially compose computations *)
bind:(C1: ST P1 A1 Q1)
(C2: x:A1, ST (P2 x) A2 (Q2 x)),
ST ( i => P1 i
(r h, Q1 r i h -> P2 r h))
A2
( r i f => x h, Q1 x i h Q2 x r h f)
27 June 2007 18
Back to our Example:
Definition postinc x := bind (read x) ( xv => bind (write x (xv + 1)) ( _ => ret xv)).
Check postinc.
27 June 2007 19
A Little Hard to Read…postinc : forall x : loc, ST (fun i : heap => (fun i0 : heap => exists v : nat, ptsto x v i0) i /\ (forall (x0 : nat) (m : heap), (fun (y : nat) (i0 m0 : heap) => m0 = i0 /\ ptsto x y i0) x0 i m -> (fun (xv : nat) (i0 : heap) => (fun i1 : heap => exists B : Type, exists w : B, ptsto x w i1) i0 /\ (forall (x1 : unit) (m0 : heap), (fun (_ : unit) (i1 m1 : heap) => m1 = update x (xv + 1) i1) x1 i0 m0 -> (fun (_ : unit) (_ : heap) => True) x1 m0)) x0 m)) (fun (y : nat) (i m : heap) => exists x0 : nat, exists h : heap, (fun (y0 : nat) (i0 m0 : heap) => m0 = i0 /\ ptsto x y0 i0) x0 i h /\ (fun (xv y0 : nat) (i0 m0 : heap) => exists x1 : unit, exists h0 : heap, (fun (_ : unit) (i1 m1 : heap) => m1 = update x (xv + 1) i1) x1 i0 h0 /\ (fun (_ : unit) (r : nat) (i1 f : heap) => r = xv /\ f = i1) x1 y0 h0 m0) x0 y h m)
27 June 2007 20
Consequence:
(* strengthen pre- and weaken post-condition *)
do : (C : ST P1 A Q1),
(i, P2 i -> P1 i
x f, Q1 x i f -> Q2 x i f) ->
ST P2 A Q2.
27 June 2007 21
Re-Typing with Do
Definition postinc x : ST (ref nat x) nat ( r i f => v, ptsto x v i ->
r = v f = update x (v+1) i):= do (xv <-- !!x ; x ::= (xv + 1) ;; ret xv) <proof-term>.
27 June 2007 22
AlternativelyProgram Definition postinc x : ST (ref nat x) nat ( r i f => v, ptsto x v i ->
r = v f = update x (v+1) i):= do (xv <-- !!x ; x ::= (xv + 1) ;; ret xv) _.
<proof-script>
27 June 2007 23
Or Even:
Program Definition postinc x : ST (ref nat x) nat ( _ _ => ref nat x):= do (xv <-- !!x ; x ::= (xv + 1) ;; ret xv) _.
27 June 2007 24
Proof Holes• Program lets us leave holes (“_”) for proofs.• After the definition, we enter the proof script
for each hole.– Can register default tactics which take care of
easy proofs, or simplify goals.– In the previous examples, tactics manage to
construct proofs automatically.– In bigger examples, at least simplify VCs to
something that’s manageable.
• Note, no need to specify pre/post-conditions if you don’t care.
27 June 2007 25
Exceptions
exn : Set.
Inductive ans(A:Set) := | Val : A -> ans A | Exn : exn -> ans A.
ST(P:pre)(A:Set)(Q:post(ans A)) : Set
27 June 2007 26
Two Ways to Return:
ret : (x:A), ST top A
( a i f => a=(Val x) f=i).
throw : (A:Set)(e:exn), ST top A
( a i f => a=(Exn e) f=i).
27 June 2007 27
Rebind:bind:(C1: ST P1 A1 Q1)
(C2: x:A1, ST (P2 x) A2 (Q2 x)),
ST ( i => P1 i
(r m, Q1 (Val r) i m -> P2 r m))
A2
( a i f => (x h, Q1 (Val x) i h Q2 x a h f)
(e, a = (Exn e) Q1 a i f)
27 June 2007 28
Catch is Similar:catch:(C1: ST P1 A Q1)
(C2: e:exn, ST (P2 e) A (Q2 x)),
ST ( i => P1 i
(e m, Q1 (Exn e) i m -> P2 e m))
A
( a i f => (e h, Q1 (Exn e) i h Q2 e a h f)
(x, a = (Val x) Q1 a i f)
27 June 2007 29
Can code Conditionals:
Program Definition If(test : {R} + {S})
(C1 : R -> ST P1 A Q1)
(C2 : S -> ST P2 A Q2) :
ST (fun i => (R -> P1 i) (S -> P2 i))
A
(fun r i f => (R Q1 r i f) (S Q2 r i f)) :=
match test with
| left pfR => do (C1 pfR) _
| right pfS => do (C2 pfS) _
end.
27 June 2007 30
Arrays:Treated as an n-vector of (distinct) locations.
vsub : (a:array n)(j:nat)(pf: j<n), loc.
(* subscript on array a at offset j *)
sub : (A:Set)(a:array n)(j:nat)(pf: j<n), ST (ref A (vsub a j pf)) A ( r i f => f = i
ptsto (vsub a j pf) r i)).
27 June 2007 31
Checked Subscript:
lessthan : (n m:nat), {n<m}+{n>=m}.
Definition csub(A:Set)(a:array n)(j:nat):= If (lessthan j n)
(fun pfj => sub a j pfj)
(fun _ => throw A IndexOutOfBounds).
27 June 2007 32
A Bigger Example: Hashtables
• Representation: array of list(key*value).– table := {len:nat ; arr:array len}
• Abstraction: (key*value) sets.– kvset : key -> value -> Prop– reps(S:kvset)(t:table)(h:heap): Prop
• Operations to create, insert, and lookup.
27 June 2007 33
Type of Create:create(n:nat) : ST top table ( y i f => (* old values in memory are preserved *) r A (v:A),ptsto r v i -> ptsto r v f (* array locations are fresh *) j (p:j<len t), unallocated(vsub (arr t) j pj) i (* returns a table that represents {} *) t:table,y=Val t reps empty_set t f).
27 June 2007 34
Type of Insert:insert : forall (k:key)(v:value)(t:table)(S:kvset), ST (reps S t) ( y i f => (* returns unit *) y=Val tt (* only changes location at (hash k) mod n *) ( r w,(r <> vsub (arr t)((hash k) mod (len t))) -> ptsto r w i -> ptsto r w f) (* array now represents {(k,v)}+S *)
reps (insert_kvset (k,v) S) t f).
27 June 2007 35
Type of Lookuplookup : (k:key)(t:table)(S:kvset), ST (reps S t) value (y i f => (* memory is unchanged *)
f=i (* throws Lookup when key isn’t in S *) (v,~(S k v) y=Exn Lookup (* or else returns some v s.t. S k v *) v, y=Val v S k v))
27 June 2007 36
Roadmap
ST(P:pre)(A:Set)(Q:post A) : SetHeaps, pre- & post-conditionsBasic monadic & state constructsExceptions, conditionals, arraysExample: hashtable• Recursion and meta-theoretic issues• Modularity & separation• Current & future work
27 June 2007 37
Recursion (CBV):
ffix :
(f : (body:x:A, ST(P x)(B x)(Q x))
(x:A), ST (P x)(B x)(Q x)),
(x:A), ST (P x)(B x)(Q x).
27 June 2007 38
For Example:Fixpoint fact(n:nat) {struct n} : nat := match n with 0 => ret 1 | S m => n * fact(m) end.
Definition spec := n:nat, ST top nat (fun y i f => f=i y=Val(fact n)).
Program Definition fact_body(fact:spec):spec := fun n => do (If (eqnat n 0) (fun _ => ret 1) (fun _ => p <-- fact (n-1) ; ret (n*p))) _.
Definition fact_rec := ffix fact_body.
27 June 2007 39
Defining ST in Coq?Can try to define:
ST P A Q := {i:heap|P h} -> {f:heap; x:A |Q x i h}
but then you sacrifice:– recursive (diverging) computations– non-deterministic computations– code that stores computations in the heap
So we will do something different.
27 June 2007 40
Consistency• No equations on computations!
– (except for monad laws)– no way to run them, even in the logic.
• Trivial model: ST = unit.– silly, but ensures we haven’t broken Coq.
• Intermediate model: – ST = predicate transformers (see Sylvain’s talk)– not big enough -- can’t store ST’s in heap!
• Denotational model: – Lars Birkedal & Rasmus Peterson– (subset of Coq corresponding to HTT)
27 June 2007 41
Operational Soundness• For HTT subset [ESOP’07]:
– give a fairly standard, small-step operational semantics
– assume “heap-consistency” of logic• we get this with the trivial model
– proved preservation & progress• Not an everyday type-soundness
proof.– fact_rec really computes factorial.– hope: scales to things like concurrency…
27 June 2007 42
Modularity
Thus far, a “big footprint” approach.– specify changes to entire heap.– not modular. – inconvenient specifications.– real nightmare for concurrency.– but meta-theory much simpler.
So we define a separation-based monad in terms of the big footprint ST.– STsep P A Q.– similar to ST, but “tight” footprint.
27 June 2007 43
STsepDefinition STsep(P:pre)(A:Set)(Q: post A):Set
:= ST (P * top) A (fun x => P -o Q x).
where:top h := True.
(P1*P2) h := h1 h2, h=h1h2 P1 h1 P2 h2.
(this h1) h2 := h1=h2.
(Q - P) i f := i1 f1 h, i=i1h f=f1h Q i1 f1 P h.(P -o Q) i f := h, (P *(this h)) i -> (Q - (this h)) i f.
Intuitively, when run in a heap P*(this h) for some h, yields a heap satisfying Q*(this h).
27 June 2007 44
Separating Specsemp h := h = empty.(xv) h := h = update x v empty.
definition snew(A:Set)(v:A): STsep emp A ( a i f => a v) := fun A v => do (new v) _.
definition sread(A:Set)(x:loc): STsep ( i => v:A, (x v) i) A ( a i f => (x a) i f=i) := fun A x => do (read x) _.
27 June 2007 45
Separating BindProgram Definition
sbind (C1 : STsep P1 A1 Q1)
(C2 : x:A1, STsep (P2 x) A2 (Q2 x)) :
STsep (i => (P1 * top) i
x h, (P1 -o Q1 x) i h -> (P2 x * top) h)
A2
(y i f => x h,(P1 -o Q1 x) i h (P2 x -o Q2 x y) h
f)
:= do (bind C1 C2) _.
27 June 2007 46
Separating DoProgram Definition sdo
sdo(C : STsep P1 A Q1)
(pf : i, P2 i -> h, (P1 * (this h)) i
(y f, (Q1 y) - (this h)) i m -> Q2 y i f)), STSep P2 A Q2
:= do C _.
27 June 2007 47
AbstractionRecord FinMap(key value:Set)(hash:key->nat):= { t : Set ; reps : set(key*value)->t->heap->Prop ; create : All n:nat, STsep emp t ( r _ => reps empty_set r) ; insert : All k v t S, STsep (reps S t) unit ( _ _ => reps (ins (k,v) S) t) ; ... }
Can build hash table, association list, splay tree, etc. that meets this interface.
27 June 2007 48
What’s Next?• Examples:
– have simple data structures (e.g., hash tables.)– can abstract over internal invariants.– need larger libraries & apps to drive
development of abstractions & tactics.
• Extraction:– can extract Haskell code now.– but want to build a good ML extractor
• no need to pay the overhead of laziness• can leverage effects in compiler
27 June 2007 49
Scaling the Language• I/O and Foreign Functions
– obviously needed for practical programming– perhaps a simple treatment of “worlds”– right sort of monad transformers?
• Concurrency– looking at transactions (a la STM) as basis– continue to use STsep for sequential code– simplifies O’Hearn-style separation treatment of
concurrency– but of course, models & soundness tricky
27 June 2007 50
Concurrency:STsep P A Q -- sequential, undoableCmd I P A Q -- parallel, not undoable
par : Cmd I P1 A1 Q1 ->
Cmd I P2 A2 Q2 ->
Cmd I (P1*P2) (A1*A2) (Q1*Q2)
atomic : STsep (I*P) A (I*Q) -> Cmd I P A Q
27 June 2007 51
Summary• ST monad in Coq
– state, [de]allocation, strong updates, exceptions, recursion (i.e., core ML++)
– specifications “inferred” for straightline code– various models– operational semantics & soundness argument
• STsep monad:– small footprint treatment of state layered on ST– gives us framing and abstraction
• Working on IO & Concurrency
27 June 2007 52
Thanks!
• Simona Ronchi Della Rocca• The TLCA PC• Ralf Treinen• Co-authors• and you.