|
| 1 | +open Effect |
| 2 | +open Effect.Deep |
| 3 | + |
| 4 | +(* This file contains a collection of attempts at replicating ML-style |
| 5 | + references using algebraic effects and handlers. The difficult thing |
| 6 | + to do is the dynamic creation of new reference cells at arbitrary |
| 7 | + types, without needing some kind of universal type or dynamic type |
| 8 | + checking. *) |
| 9 | + |
| 10 | +module type Type = sig type t end |
| 11 | +module Int = struct type t = int let compare = compare end |
| 12 | + |
| 13 | +module LocalState (R : sig type t end) = struct |
| 14 | + type reff = R.t |
| 15 | + type _ eff += New : int -> R.t eff |
| 16 | + type _ eff += Get : R.t -> int eff |
| 17 | + type _ eff += Put : R.t * int -> unit eff |
| 18 | +end |
| 19 | + |
| 20 | +module type StateOps = sig |
| 21 | + type reff |
| 22 | + type _ eff += New : int -> reff eff |
| 23 | + type _ eff += Get : reff -> int eff |
| 24 | + type _ eff += Put : reff * int -> unit eff |
| 25 | +end |
| 26 | + |
| 27 | +(**********************************************************************) |
| 28 | +(* version 1 : doesn't work, because declaration of new effect names |
| 29 | + is generative, so the handler and the client get different versions of |
| 30 | + the 'New', 'Get' and 'Put' effects. *) |
| 31 | + |
| 32 | +let run main = |
| 33 | + let module S = LocalState (Int) in |
| 34 | + let module IM = Map.Make (Int) in |
| 35 | + let comp = |
| 36 | + match main (module Int : Type) with |
| 37 | + | effect (S.New i), k -> |
| 38 | + fun s -> let r = fst (IM.max_binding s) + 1 |
| 39 | + in continue k r (IM.add r i s) |
| 40 | + | effect (S.Get r), k -> |
| 41 | + fun s -> continue k (IM.find r s) s |
| 42 | + | effect (S.Put (r, i)), k -> |
| 43 | + fun s -> continue k () (IM.add r i s) |
| 44 | + | x -> fun s -> x |
| 45 | + in |
| 46 | + comp IM.empty |
| 47 | + |
| 48 | +let main (module T : Type) = |
| 49 | + let module S = LocalState(T) in |
| 50 | + let x = perform (S.New 1) in |
| 51 | + perform (S.Put (x, 5)); |
| 52 | + perform (S.Get x) |
| 53 | + |
| 54 | +(**********************************************************************) |
| 55 | +(* version 2 : working creation of freshly generated state cells, but |
| 56 | + only an int type. *) |
| 57 | + |
| 58 | +let run2 main = |
| 59 | + let module S = LocalState (Int) in |
| 60 | + let module IM = Map.Make (Int) in |
| 61 | + let comp = |
| 62 | + match main (module S : StateOps) with |
| 63 | + | effect (S.New i), k -> |
| 64 | + fun s -> |
| 65 | + let r = if IM.is_empty s then 0 else fst (IM.max_binding s) + 1 |
| 66 | + in continue k r (IM.add r i s) |
| 67 | + | effect (S.Get r), k -> |
| 68 | + fun s -> continue k (IM.find r s) s |
| 69 | + | effect (S.Put (r, i)), k -> |
| 70 | + fun s -> continue k () (IM.add r i s) |
| 71 | + | x -> fun s -> x |
| 72 | + in |
| 73 | + comp IM.empty |
| 74 | + |
| 75 | +let main2 (module S : StateOps) = |
| 76 | + let open S in |
| 77 | + let x = perform (New 1) in |
| 78 | + perform (Put (x, 5)); |
| 79 | + perform (Get x) |
| 80 | + |
| 81 | +(**********************************************************************) |
| 82 | +(* version 3, static creation of new state cells, requiring nested |
| 83 | + handlers. Similar to the example in "state.ml". *) |
| 84 | +module type GetPutOps = sig |
| 85 | + type t |
| 86 | + type _ eff += Get : t eff |
| 87 | + type _ eff += Put : t -> unit eff |
| 88 | +end |
| 89 | + |
| 90 | +module MakeGetPut (T : sig type t end) () = struct |
| 91 | + type t = T.t |
| 92 | + type _ eff += Get : t eff |
| 93 | + type _ eff += Put : t -> unit eff |
| 94 | +end |
| 95 | + |
| 96 | +let run3 (type a) (module S : GetPutOps with type t = a) (s : a) main = |
| 97 | + let module IM = Map.Make (Int) in |
| 98 | + let comp = |
| 99 | + match main () with |
| 100 | + | effect S.Get, k -> |
| 101 | + fun (s : S.t) -> continue k s s |
| 102 | + | effect (S.Put i), k -> |
| 103 | + fun s -> continue k () i |
| 104 | + | x -> fun s -> x |
| 105 | + in |
| 106 | + comp s |
| 107 | + |
| 108 | +module S1 = MakeGetPut (struct type t = int end) () |
| 109 | +module S2 = MakeGetPut (struct type t = string end) () |
| 110 | + |
| 111 | +let test3 () = |
| 112 | + perform (S1.Put 5); |
| 113 | + let x = perform (S1.Get) in |
| 114 | + perform (S2.Put (string_of_int x ^ "xx")); |
| 115 | + perform S2.Get |
| 116 | + |
| 117 | +(* XXX avsm: disabled pending port to multicont (uses clone_continuation) |
| 118 | +
|
| 119 | +(**********************************************************************) |
| 120 | +(* version 4. Uses dynamic creation of new effect names to simulate |
| 121 | + the creation of new reference cells. Initially, there is only one |
| 122 | + effect 'New', which can be used to dynamically create new effect |
| 123 | + names. The handler for 'New' wraps the continuation in a new |
| 124 | + handler that handles the freshly generated effect names. This setup |
| 125 | + yields the same interface as ML refs, except that there is no way |
| 126 | + to compare references for equality. This is because cells are |
| 127 | + represeted as objects with a pair of a 'write' method and a 'read' |
| 128 | + method, so it is possible to create new references that reference |
| 129 | + the same underlying data without the access objects being |
| 130 | + equal. This is similar to the situation in Idealised Algol, where |
| 131 | + variables are ways to affect the state, but have no independent |
| 132 | + existence of their own. |
| 133 | +
|
| 134 | + Compared to the example in "ref.ml", this implementation does not |
| 135 | + require a universal type, nor does it have "impossible" cases. |
| 136 | +
|
| 137 | + This example also includes an unneccessary extra 'Choice' effect to |
| 138 | + demonstrate the combination of other effects with state in the same |
| 139 | + handler. This uses the experimental Obj.clone_continuation function to clone |
| 140 | + continuations. *) |
| 141 | +type 'a reff = < get : 'a; put : 'a -> unit; internals : (module GetPutOps with type t = 'a) > |
| 142 | +
|
| 143 | +effect New : 'a -> 'a rEffect.t |
| 144 | +effect Choice : bool |
| 145 | +
|
| 146 | +let run4 main = |
| 147 | + let donew : type a b. (a reff, b) continuation -> a -> b = fun k -> |
| 148 | + let module Ops = MakeGetPut (struct type t = a end) () in |
| 149 | + let cell = object |
| 150 | + method get = perform Ops.Get |
| 151 | + method put x = perform (Ops.Put x) |
| 152 | + method internals = (module Ops : GetPutOps with type t = a) |
| 153 | + end |
| 154 | + in |
| 155 | + match continue k cell with |
| 156 | + | effect Ops.Get k -> fun s -> continue k s s |
| 157 | + | effect (Ops.Put v) k -> fun s -> continue k () v |
| 158 | + | x -> fun s -> x |
| 159 | + in |
| 160 | + match main () with |
| 161 | + | effect (New v) k -> donew k v |
| 162 | + | effect (Choice) k -> let k' = Obj.clone_continuation k in continue k true; continue k' false |
| 163 | + | x -> x |
| 164 | +
|
| 165 | +let newref i = perform (New i) |
| 166 | +
|
| 167 | +let (:=) r x = r#put x |
| 168 | +
|
| 169 | +let (!) r = r#get |
| 170 | +
|
| 171 | +let test4 () = |
| 172 | + let a = newref 0 in |
| 173 | + let b = newref "str" in |
| 174 | + if perform Choice then |
| 175 | + begin a := String.length !b; |
| 176 | + b := string_of_int !a; |
| 177 | + print_endline !b |
| 178 | + end |
| 179 | + else |
| 180 | + print_endline !b |
| 181 | +*) |
0 commit comments