My PhD dissertation at the University of Edinburgh, Scotland
https://www.dhil.net/research/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
397 lines
9.6 KiB
397 lines
9.6 KiB
|
5 years ago
|
(* Companion for "State of effectful programming"
|
||
|
|
Tested with OCaml 4.10.0+multicore. *)
|
||
|
|
|
||
|
|
(* Generic direct-style incr_even *)
|
||
|
|
let even : int -> bool
|
||
|
|
= fun n -> n mod 2 = 0
|
||
|
|
|
||
|
|
let incr_even : (unit -> int) * (int -> unit) -> unit -> bool
|
||
|
|
= fun (get, put) () ->
|
||
|
|
let st = get () in
|
||
|
|
put (1 + st);
|
||
|
|
even st
|
||
|
|
|
||
|
|
(* Delimited control *)
|
||
|
|
module Prompt : sig
|
||
|
|
type 'a t
|
||
|
|
val make : unit -> 'a t
|
||
|
|
val reify : 'a t -> (('b -> 'a) -> 'a) -> 'b
|
||
|
|
val install : 'a t -> (unit -> 'a) -> 'a
|
||
|
|
end = struct
|
||
|
|
type 'a t = {
|
||
|
|
install : (unit -> 'a) -> 'a;
|
||
|
|
reify : 'b. (('b -> 'a) -> 'a) -> 'b
|
||
|
|
}
|
||
|
|
|
||
|
|
let make (type a) () =
|
||
|
|
let module M = struct
|
||
|
|
effect Prompt : (('b -> a) -> a) -> 'b
|
||
|
|
end
|
||
|
|
in
|
||
|
|
let reify f = perform (M.Prompt f) in
|
||
|
|
let install f =
|
||
|
|
match f () with
|
||
|
|
| x -> x
|
||
|
|
| effect (M.Prompt f) k -> f (continue k)
|
||
|
|
in
|
||
|
|
{ install; reify }
|
||
|
|
|
||
|
|
let install { install; _ } = install
|
||
|
|
let reify { reify; _ } = reify
|
||
|
|
let resume k v = continue k v
|
||
|
|
end
|
||
|
|
|
||
|
|
module type CTRL = sig
|
||
|
|
type ans
|
||
|
|
val reset : (unit -> ans) -> ans
|
||
|
|
val shift : (('a -> ans) -> ans) -> 'a
|
||
|
|
end
|
||
|
|
|
||
|
|
module Ctrl(R : sig type ans end) : sig
|
||
|
|
include CTRL with type ans = R.ans
|
||
|
|
end = struct
|
||
|
|
type ans = R.ans
|
||
|
|
|
||
|
|
let p : ans Prompt.t = Prompt.make ()
|
||
|
|
|
||
|
|
let reset m =
|
||
|
|
Prompt.install p m
|
||
|
|
|
||
|
|
let shift f =
|
||
|
|
Prompt.reify p
|
||
|
|
(fun k ->
|
||
|
|
Prompt.install p
|
||
|
|
(fun () ->
|
||
|
|
f (fun x ->
|
||
|
|
Prompt.install p
|
||
|
|
(fun () -> k x))))
|
||
|
|
end
|
||
|
|
|
||
|
|
module CtrlState
|
||
|
|
(S : sig type s end)
|
||
|
|
(R : sig type ans end): sig
|
||
|
|
type s = S.s
|
||
|
|
type ans = s -> R.ans * s
|
||
|
|
|
||
|
|
val get : unit -> s
|
||
|
|
val put : s -> unit
|
||
|
|
|
||
|
|
val run : (unit -> R.ans) -> ans
|
||
|
|
end = struct
|
||
|
|
type s = S.s
|
||
|
|
type ans = s -> R.ans * s
|
||
|
|
module Ctrl = Ctrl(struct type nonrec ans = ans end)
|
||
|
|
|
||
|
|
let get () = Ctrl.shift (fun k -> fun st -> k st st)
|
||
|
|
let put st' = Ctrl.shift (fun k -> fun st -> k () st')
|
||
|
|
|
||
|
|
let run m =
|
||
|
|
Ctrl.reset
|
||
|
|
(fun () ->
|
||
|
|
let x = m () in
|
||
|
|
fun st -> (x, st))
|
||
|
|
end
|
||
|
|
|
||
|
|
module CtrlIntState = CtrlState(struct type s = int end)(struct type ans = bool end)
|
||
|
|
|
||
|
|
(* Monadic programming *)
|
||
|
|
module type MONAD = sig
|
||
|
|
type 'a t
|
||
|
|
val return : 'a -> 'a t
|
||
|
|
val (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||
|
|
end
|
||
|
|
|
||
|
|
(** State monad **)
|
||
|
|
module type STATE_MONAD = sig
|
||
|
|
type ans
|
||
|
|
type s
|
||
|
|
include MONAD
|
||
|
|
|
||
|
|
val get : unit -> s t
|
||
|
|
val put : s -> unit t
|
||
|
|
val run : (unit -> ans t) -> s -> ans * s
|
||
|
|
end
|
||
|
|
|
||
|
|
module StateMonad(S : sig type s end)(R : sig type ans end): sig
|
||
|
|
include STATE_MONAD with type s = S.s
|
||
|
|
and type ans = R.ans
|
||
|
|
end = struct
|
||
|
|
type ans = R.ans
|
||
|
|
type s = S.s
|
||
|
|
type 'a t = s -> 'a * s
|
||
|
|
|
||
|
|
let return : 'a -> 'a t
|
||
|
|
= fun x -> fun st -> (x, st)
|
||
|
|
|
||
|
|
let (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||
|
|
= fun m k -> fun st ->
|
||
|
|
let (x, st') = m st in
|
||
|
|
k x st'
|
||
|
|
|
||
|
|
let get : unit -> s t
|
||
|
|
= fun () st -> (st, st)
|
||
|
|
|
||
|
|
let put : s -> unit t
|
||
|
|
= fun st st' -> ((), st)
|
||
|
|
|
||
|
|
let run : (unit -> ans t) -> s -> ans * s
|
||
|
|
= fun m st -> m () st
|
||
|
|
end
|
||
|
|
|
||
|
|
module IntStateMonad = StateMonad(struct type s = int end)(struct type ans = bool end)
|
||
|
|
|
||
|
|
(** Continuation monad **)
|
||
|
|
module type CONTINUATION_MONAD = sig
|
||
|
|
type r
|
||
|
|
include MONAD with type 'a t = ('a -> r) -> r
|
||
|
|
end
|
||
|
|
|
||
|
|
module ContinuationMonad(R : sig type ans end): sig
|
||
|
|
include CONTINUATION_MONAD with type r = R.ans
|
||
|
|
end = struct
|
||
|
|
type r = R.ans
|
||
|
|
type 'a t = ('a -> r) -> r
|
||
|
|
|
||
|
|
let return : 'a -> 'a t
|
||
|
|
= fun x -> fun k -> k x
|
||
|
|
|
||
|
|
let (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||
|
|
= fun m k -> fun c ->
|
||
|
|
m (fun x -> k x c)
|
||
|
|
end
|
||
|
|
|
||
|
|
module ContinuationStateMonad
|
||
|
|
(S : sig type s end)
|
||
|
|
(R : sig type ans end): sig
|
||
|
|
type s = S.s
|
||
|
|
type ans = R.ans
|
||
|
|
include CONTINUATION_MONAD with type r = s -> ans * s
|
||
|
|
|
||
|
|
val get : unit -> s t
|
||
|
|
val put : s -> unit t
|
||
|
|
val run : (unit -> ans t) -> s -> ans * s
|
||
|
|
end = struct
|
||
|
|
type s = S.s
|
||
|
|
type ans = R.ans
|
||
|
|
module ContinuationMonad : CONTINUATION_MONAD with type r = s -> ans * s
|
||
|
|
= ContinuationMonad(struct type nonrec ans = s -> ans * s end)
|
||
|
|
include ContinuationMonad
|
||
|
|
|
||
|
|
let get : unit -> s t
|
||
|
|
= fun () -> fun k -> fun st -> k st st
|
||
|
|
|
||
|
|
let put : s -> unit t
|
||
|
|
= fun st' -> fun k -> fun st -> k () st'
|
||
|
|
|
||
|
|
let run : (unit -> R.ans t) -> s -> R.ans * s =
|
||
|
|
fun m st -> m () (fun x -> fun st -> (x, st)) st
|
||
|
|
end
|
||
|
|
|
||
|
|
module ContinuationIntStateMonad
|
||
|
|
= ContinuationStateMonad(struct type s = int end)(struct type ans = bool end)
|
||
|
|
|
||
|
|
(** Free monad **)
|
||
|
|
module type FUNCTOR = sig
|
||
|
|
type 'a t
|
||
|
|
val fmap : ('a -> 'b) -> 'a t -> 'b t
|
||
|
|
end
|
||
|
|
|
||
|
|
module type FREE_MONAD = sig
|
||
|
|
type 'a op
|
||
|
|
type 'a free = Return of 'a
|
||
|
|
| Op of 'a free op
|
||
|
|
|
||
|
|
include MONAD with type 'a t = 'a free
|
||
|
|
|
||
|
|
val do' : 'a op -> 'a free
|
||
|
|
end
|
||
|
|
|
||
|
|
module FreeMonad(F : FUNCTOR) : sig
|
||
|
|
include FREE_MONAD with type 'a op = 'a F.t
|
||
|
|
end = struct
|
||
|
|
type 'a op = 'a F.t
|
||
|
|
type 'a free = Return of 'a
|
||
|
|
| Op of 'a free F.t
|
||
|
|
|
||
|
|
type 'a t = 'a free
|
||
|
|
|
||
|
|
let return : 'a -> 'a t
|
||
|
|
= fun x -> Return x
|
||
|
|
|
||
|
|
let rec (>>=) : 'a t -> ('a -> 'b t) -> 'b t
|
||
|
|
= fun m k ->
|
||
|
|
match m with
|
||
|
|
| Return x -> k x
|
||
|
|
| Op y -> Op (F.fmap (fun m' -> m' >>= k) y)
|
||
|
|
|
||
|
|
let do' : 'a F.t -> 'a free
|
||
|
|
= fun op -> Op (F.fmap (fun x -> Return x) op)
|
||
|
|
end
|
||
|
|
|
||
|
|
module type FREE_STATE = sig
|
||
|
|
type s
|
||
|
|
type 'r opsig = Get of (s -> 'r)
|
||
|
|
| Put of s * (unit -> 'r)
|
||
|
|
include FUNCTOR with type 'r t = 'r opsig
|
||
|
|
end
|
||
|
|
|
||
|
|
module FreeState(S : sig type s end) = struct
|
||
|
|
type s = S.s
|
||
|
|
type 'r opsig = Get of (s -> 'r)
|
||
|
|
| Put of s * (unit -> 'r)
|
||
|
|
type 'r t = 'r opsig
|
||
|
|
|
||
|
|
let fmap : ('a -> 'b) -> 'a t -> 'b t
|
||
|
|
= fun f op ->
|
||
|
|
match op with
|
||
|
|
| Get k -> Get (fun st -> f (k st))
|
||
|
|
| Put (st', k) -> Put (st', fun st -> f (k ()))
|
||
|
|
end
|
||
|
|
|
||
|
|
module FreeIntStateMonad: sig
|
||
|
|
include STATE_MONAD with type s = int
|
||
|
|
and type ans = bool
|
||
|
|
end = struct
|
||
|
|
|
||
|
|
module rec FreeIntState : FREE_STATE with type s = int
|
||
|
|
= FreeState(struct type s = int end)
|
||
|
|
and FreeIntStateMonad : FREE_MONAD with type 'r op = 'r FreeIntState.opsig
|
||
|
|
= FreeMonad(FreeIntState)
|
||
|
|
|
||
|
|
open FreeIntState
|
||
|
|
include FreeIntStateMonad
|
||
|
|
|
||
|
|
type s = int
|
||
|
|
type ans = bool
|
||
|
|
|
||
|
|
let get : unit -> s t
|
||
|
|
= fun () -> do' (Get (fun st -> st))
|
||
|
|
|
||
|
|
let put : s -> unit t
|
||
|
|
= fun st -> do' (Put (st, fun () -> ()))
|
||
|
|
|
||
|
|
let rec run : (unit -> ans t) -> s -> ans * s
|
||
|
|
= fun m st ->
|
||
|
|
match m () with
|
||
|
|
| Return x -> (x, st)
|
||
|
|
| Op (Get k) -> run (fun () -> k st) st
|
||
|
|
| Op (Put (st', k)) -> run k st'
|
||
|
|
end
|
||
|
|
|
||
|
|
(** Monadic reflection **)
|
||
|
|
module Reflect
|
||
|
|
(M : MONAD)
|
||
|
|
(R : sig type ans end): sig
|
||
|
|
type ans = R.ans
|
||
|
|
|
||
|
|
val reify : (unit -> ans) -> ans M.t
|
||
|
|
val reflect : 'a M.t -> 'a
|
||
|
|
|
||
|
|
end = struct
|
||
|
|
type ans = R.ans
|
||
|
|
effect Reflect : 'a M.t -> 'a
|
||
|
|
|
||
|
|
let reify : (unit -> ans) -> ans M.t
|
||
|
|
= fun f ->
|
||
|
|
let open M in
|
||
|
|
match f () with
|
||
|
|
| x -> return x
|
||
|
|
| effect (Reflect m) k -> m >>= (continue k)
|
||
|
|
|
||
|
|
let reflect : 'a M.t -> 'a
|
||
|
|
= fun m ->
|
||
|
|
perform (Reflect m)
|
||
|
|
end
|
||
|
|
|
||
|
|
module ReflectIntStateMonad
|
||
|
|
= Reflect(IntStateMonad)(struct type ans = bool end)
|
||
|
|
|
||
|
|
module ReflectIntState = struct
|
||
|
|
open ReflectIntStateMonad
|
||
|
|
|
||
|
|
let get : unit -> int
|
||
|
|
= fun () -> reflect (IntStateMonad.get ())
|
||
|
|
|
||
|
|
let put : int -> unit
|
||
|
|
= fun st -> reflect (IntStateMonad.put st)
|
||
|
|
|
||
|
|
let run : (unit -> bool) -> int -> bool * int
|
||
|
|
= fun m st -> IntStateMonad.run (fun () -> reify m) st
|
||
|
|
end
|
||
|
|
|
||
|
|
(* Generic monadic incr_even *)
|
||
|
|
module MonadExample(T : STATE_MONAD with type s = int) = struct
|
||
|
|
let incr_even : unit -> bool T.t
|
||
|
|
= fun () ->
|
||
|
|
let open T in
|
||
|
|
(get ()) >>= (fun st -> put (1 + st)
|
||
|
|
>>= (fun () -> return (even st)))
|
||
|
|
end
|
||
|
|
|
||
|
|
(** Effect handlers **)
|
||
|
|
module type STATE_HANDLER = sig
|
||
|
|
type s
|
||
|
|
|
||
|
|
val get : unit -> s
|
||
|
|
val put : s -> unit
|
||
|
|
val run : (unit -> 'a) -> s -> 'a * s
|
||
|
|
end
|
||
|
|
|
||
|
|
module StateHandler(S : sig type s end) : STATE_HANDLER with type s = S.s = struct
|
||
|
|
type s = S.s
|
||
|
|
|
||
|
|
effect Put : s -> unit
|
||
|
|
let put st = perform (Put st)
|
||
|
|
|
||
|
|
effect Get : unit -> s
|
||
|
|
let get () = perform (Get ())
|
||
|
|
|
||
|
|
let run
|
||
|
|
= fun m st ->
|
||
|
|
let f = match m () with
|
||
|
|
| x -> (fun st -> (x, st))
|
||
|
|
| effect (Put st') k -> (fun st -> continue k () st')
|
||
|
|
| effect (Get ()) k -> (fun st -> continue k st st)
|
||
|
|
in f st
|
||
|
|
end
|
||
|
|
|
||
|
|
module IntStateHandler = StateHandler(struct type s = int end)
|
||
|
|
|
||
|
|
let run_examples () =
|
||
|
|
let examples = [
|
||
|
|
"builtin", (fun st ->
|
||
|
|
let st = ref st in let v = !st in st := 1 + v; (even v, !st));
|
||
|
|
"pure state passing", (fun st -> (even st, 1 + st));
|
||
|
|
"shift/reset", (fun st ->
|
||
|
|
CtrlIntState.run (incr_even CtrlIntState.(get, put)) st);
|
||
|
|
"state monad", (fun st ->
|
||
|
|
let module MonadStateExample = MonadExample(IntStateMonad) in
|
||
|
|
IntStateMonad.run MonadStateExample.incr_even st);
|
||
|
|
"continuation monad", (fun st ->
|
||
|
|
let module ContinuationMonadExample = MonadExample(ContinuationIntStateMonad) in
|
||
|
|
ContinuationIntStateMonad.run ContinuationMonadExample.incr_even st);
|
||
|
|
"free monad", (fun st ->
|
||
|
|
let module FreeMonadExample = MonadExample(FreeIntStateMonad) in
|
||
|
|
FreeIntStateMonad.run FreeMonadExample.incr_even st);
|
||
|
|
"monadic reflection", (fun st ->
|
||
|
|
ReflectIntState.run (incr_even ReflectIntState.(get, put)) st);
|
||
|
|
"state handler", (fun st ->
|
||
|
|
IntStateHandler.run (incr_even IntStateHandler.(get, put)) st) ]
|
||
|
|
in
|
||
|
|
List.map (fun (s, f) -> (s, f 4)) examples
|
||
|
|
(* module IntStateMRefl : MREFL with type ans := bool and type 'a t = 'a IntState.t
|
||
|
|
* = MRefl(struct type ans = bool end)(IntState)
|
||
|
|
*
|
||
|
|
* let get () = IntStateMRefl.reflect (IntState.get ())
|
||
|
|
* let put st = IntStateMRefl.reflect (IntState.put st)
|
||
|
|
* let run m st = IntState.run (IntStateMRefl.reify m) st
|
||
|
|
*
|
||
|
|
* let even : int -> bool
|
||
|
|
* = fun n -> n mod 2 = 0
|
||
|
|
*
|
||
|
|
* let incr_even : unit -> bool
|
||
|
|
* = fun () ->
|
||
|
|
* let st = get () in
|
||
|
|
* put (1 + st);
|
||
|
|
* even st *)
|