an overview of mezzo
play

An overview of Mezzo Franois Pottier INRIA Bertinoro, June 2015 1 - PowerPoint PPT Presentation

An overview of Mezzo Franois Pottier INRIA Bertinoro, June 2015 1 / 91 Acknowledgements Jonathan Protzenko, Thibaut Balabonski, Henri Chataing, Armal Guneau, Cyprien Mangin. 2 / 91 What is Mezzo? Try it out in your browser: Or


  1. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . implicit transition from abstract frozen a . frozen to frozen * frozen fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  2. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . explicit transition abstract frozen a . into writable fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  3. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . set requires r (dynamic) abstract frozen a . and r @ writable (static) fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  4. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . consumes keyword means abstract frozen a . r @ writable NOT returned fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  5. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . duplicable a abstract frozen a . is a permission fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  6. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . explicit transition from abstract frozen a . writable to frozen fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  7. Specification of write-once refs This protocol has two states and four transitions. . . . 25 / 91 This is the interface file woref.mzi : abstract writable . get r requires r @ frozen a abstract frozen a . fact duplicable ( frozen a ) . val new : () -> writable . val set : [ a ] (consumes r : . writable . , x : a | duplicable . a ) -> (| r @ frozen a ) . val get : [ a ] frozen a -> a .

  8. Implementation . 26 / 91 This is the implementation file woref.mz : data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  9. Implementation . . 26 / 91 a field of type () This is the implementation file woref.mz : data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  10. Implementation . . 26 / 91 a field of type a This is the implementation file woref.mz : where a must be duplicable data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  11. Implementation . . 26 / 91 initially, This is the implementation file woref.mz : r @ writable data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  12. Implementation . . 26 / 91 hence, This is the implementation file woref.mz : r @ Writable { contents: () } data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  13. Implementation . . 26 / 91 after the assignment, This is the implementation file woref.mz : r @ Writable { contents: =x } data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  14. Implementation . . 26 / 91 hence, This is the implementation file woref.mz : r @ Writable { contents: a } data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  15. Implementation . . 26 / 91 after the tag update, This is the implementation file woref.mz : r @ Frozen { contents: a } data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  16. Implementation . . 26 / 91 hence, This is the implementation file woref.mz : r @ frozen a data mutable writable = Writable { contents : () . } data frozen a = Frozen { contents : . ( a | duplicable a ) } val new () : writable = Writable { contents = () } val set [ a ] (consumes r : writable , x : a | duplicable a ) : (| r @ frozen a ) = . r . contents <- x ; . tag of r <- Frozen . (* this is a no-op *) val get [ a ] ( r : frozen a ) : a = r . contents

  17. A first example and a few principles Mezzo: the good and the bad 27 / 91

  18. The good The uniqueness of read/write permissions: 28 / 91 • rules out several categories of errors: • data races; hence, shared-memory concurrency is safe ; • representation exposure; • violations of (certain) object protocols. • allows the type of an object to vary with time, which enables: • explicit memory re-use; • gradual initialization; • describing (certain) object protocols.

  19. The good Here are some other positive aspects: 29 / 91 • all of the power of ML, and more; • higher-order functions, pattern matching, polymorphism, etc. • no need to annotate types with owners; • to have a permission is to own • ownership transfer is easy; • just pass (or return, or store, or extract) a permission • no need to annotate function types with effects. • just pass and return a permission

  20. The good Moving an element into or out of a container is easy. Here is a typical container interface: 30 / 91 abstract bag a val new : [ a ] () -> bag a val insert : [ a ] ( bag a , consumes a ) -> () val extract : [ a ] bag a -> option a

  21. The bad restricted to duplicable elements: This affects user-defined data structures, arrays, regions, etc. 31 / 91 The discipline forbids sharing mutable data. For this reason, borrowing an element from a container is typically val find : [ a ] duplicable a => ( a -> bool ) -> list a -> option a

  22. The bad Fortunately, 32 / 91 • there is no restriction on the use of immutable data; • there are several ways of sharing mutable data: • (static) nesting; regions; • (dynamic) adoption & abandon; • (dynamic) locks.

  23. Outline A first example and a few principles Algebraic data structures (More) Principles Computing the length of a list Melding mutable lists Concatenating immutable lists Sharing mutable data Conclusion 33 / 91

  24. Algebraic data structures (More) Principles 34 / 91

  25. Immutable lists The algebraic data type of immutable lists is defined as in ML: 35 / 91 data list a = | Nil | Cons { head : a ; tail : list a }

  26. Mutable lists To define a type of mutable lists, one adds a keyword: 36 / 91 data mutable mlist a = | MNil | MCons { head : a ; tail : mlist a }

  27. Examples For instance, read/write access to the elements, which are distinct cells. 37 / 91 • x @ list int provides (read) access to an immutable list of integers, rooted at x . • x @ mlist int provides (exclusive, read/write) access to a mutable list of integers at x . • x @ list ( ref int ) offers read access to the spine and

  28. Permission refinement Permission refinement takes place at case analysis. . . . . In contrast, traditional separation logic has untagged union. . 38 / 91 match xs with | MNil -> ... | MCons -> let x = xs . head in ... end

  29. Permission refinement . . . In contrast, traditional separation logic has untagged union. . Permission refinement takes place at case analysis. 38 / 91 . . a nominal permission: match xs with xs @ mlist a | MNil -> ... | MCons -> let x = xs . head in ... end

  30. Permission refinement . . . In contrast, traditional separation logic has untagged union. . Permission refinement takes place at case analysis. 38 / 91 . . a structural permission: match xs with xs @ MNil | MNil -> ... | MCons -> let x = xs . head in ... end

  31. Permission refinement . . . In contrast, traditional separation logic has untagged union. . Permission refinement takes place at case analysis. 38 / 91 . . another structural permission: match xs with xs @ MCons { head: a; tail: mlist a } | MNil -> ... | MCons -> let x = xs . head in ... end

  32. Permission refinement . . . In contrast, traditional separation logic has untagged union. Permission refinement takes place at case analysis. . . . 38 / 91 automatically expanded to: match xs with xs @ MCons { head: (=h); tail: (=t) } | MNil -> * h @ a * t @ mlist a ... | MCons -> let x = xs . head in ... end

  33. Permission refinement . . . In contrast, traditional separation logic has untagged union. Permission refinement takes place at case analysis. . . . 38 / 91 or (sugar): match xs with xs @ MCons { head = h; tail = t } | MNil -> * h @ a * t @ mlist a ... | MCons -> let x = xs . head in ... end

  34. Permission refinement . . . In contrast, traditional separation logic has untagged union. Permission refinement takes place at case analysis. . 38 / 91 . . so, after the read access: match xs with xs @ MCons { head = h; tail = t } | MNil -> * h @ a * t @ mlist a ... * x = h | MCons -> let x = xs . head in ... end

  35. Principles This illustrates two mechanisms: yielding a structural permission. yielding separate permissions for the block and its fields. These reasoning steps are implicit and reversible. 39 / 91 • A nominal permission can be unfolded and refined , • A structural permission can be decomposed ,

  36. Algebraic data structures Computing the length of a list 40 / 91

  37. Interface It should be understood as follows: 41 / 91 Here is the type of the length function for mutable lists. val length : [ a ] mlist a -> int • length requires one argument xs , along with the permission xs @ mlist a . • length returns one result n , along with the permission xs @ mlist a * n @ int .

  38. Implementation . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs )

  39. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) initially: xs @ mlist a

  40. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) upon entry into the first branch: xs @ MNil

  41. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) upon exit of the first branch: xs @ MNil

  42. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) upon exit of the first branch: xs @ mlist a

  43. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) upon entry into the second branch: xs @ MCons { head = h; tail = t } h @ a t @ mlist a

  44. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) after the call, nothing has changed: xs @ MCons { head = h; tail = t } h @ a t @ mlist a

  45. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) thus, by recombining: xs @ MCons { head: a; tail: mlist a }

  46. Implementation . . . 42 / 91 val rec length_aux [ a ] ( accu : int , xs : mlist a ) : int = match xs with . | MNil -> . accu . | MCons -> . length_aux ( accu + 1, xs . tail ) . end val length [ a ] ( xs : mlist a ) : int = length_aux (0, xs ) thus, by folding: xs @ mlist a

  47. Tail recursion versus iteration The analysis of this code is surprisingly simple. a loop in disguise. a list segment behind us. framed out . Recursive reasoning, iterative execution. 43 / 91 • This is a tail-recursive function, i.e., • As we go, there is a list ahead of us and • Ownership of the latter is implicit , i.e.,

  48. Algebraic data structures Melding mutable lists 44 / 91

  49. Melding mutable lists (1/2) . 45 / 91 val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  50. Melding mutable lists (1/2) . . 45 / 91 xs is not consumed: at the end, it is still a valid non-empty list val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  51. Melding mutable lists (1/2) . . 45 / 91 at the end, ys is accessible through xs, hence must no longer be used directly val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  52. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail = t } t @ MNil ys @ mlist a val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  53. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail = ys } t @ MNil ys @ mlist a val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  54. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail: mlist a } t @ MNil val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  55. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail: mlist a } val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  56. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail = t } t @ MCons { head: a; tail: mlist a } ys @ mlist a val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  57. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail = t } t @ MCons { head: a; tail: mlist a } val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  58. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail = t } t @ mlist a val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  59. Melding mutable lists (1/2) . . 45 / 91 xs @ MCons { head: a; tail: mlist a } val rec meld_aux [ a ] ( xs : . MCons { head : a ; tail : mlist a }, consumes . ys : mlist a ) : () = match xs . tail with | MNil -> . xs . tail <- ys . | MCons -> . meld_aux ( xs . tail , ys ) . end

  60. 46 / 91 Melding mutable lists (2/2) val meld [ a ] (consumes xs : mlist a , consumes ys : mlist a ) : mlist a = match xs with | MNil -> ys | MCons -> meld_aux ( xs , ys ); xs end

  61. Algebraic data structures Concatenating immutable lists 47 / 91

  62. Three states . . . . . . . . . 48 / 91 . . . An MCons cell: MCons • mutable, head • uninitialized tail , tail • type: MCons { head: a; tail: () } An isolated Cons cell: Cons • immutable, head • not the start of a well-formed list, tail • type: Cons { head: a; tail = t } A list cell: Cons head • immutable, tail • the start of a well-formed list, • type list a

  63. The big picture . . . . . . . . . . . . . . 49 / 91 . . . . . . . MCons head tail Cons Cons Cons Cons Cons head head head head head tail tail tail tail tail ys xs

  64. The big picture . . . . . . . . . . . . . . . . 49 / 91 . . . . . . . . MCons MCons head head tail tail Cons Cons Cons Cons Cons head head head head head tail tail tail tail tail ys xs

Recommend


More recommend