an overview of mezzo

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


Explore More Topics

Stay informed with curated content and fresh updates.