Rea
This is a framework for generic composable effectful asynchronous programming basically using only objects and polymorphic variants.
This whole framework and module is really designed to allow it to be used by opening the module:
open Rea
This brings binding operators, such as let*
, other operators, such as (>>=)
, as well as a large number of other combinators, such as bind
, and types, such as bind'
, into scope. The justification for this is that they are generic, or polymorphic with respect to the effect representation, and can easily subsume most other monadic libraries. Entire applications can be written so that no other binding operators need to be used.
WARNING: Many of the combinators are documented concisely in the format "X
is equivalent to Y
". The equivalence should only be assumed to hold modulo more or less obvious OCaml evaluation order differences.
The effect framework in this section is almost entirely free of concrete implementation details and should theoretically be usable in a wide variety of contexts including for wrapping existing monadic libraries.
The effect framework can also be extended by users. Consider the map
effect. It consists of
map'm
effect signature type abbreviation,map'
capability mix-in,map
combinator, andmonad'd
.And, of course, various interpreters implement the map'
capability. The bottom line is that nothing prevents user defined extensions following the same pattern.
Abstract effect s
ignature represents the application ('e, 'a) 'R
of the higher-kinded effect representation type constructor 'R
to the error 'e
and answer 'a
types.
Basic use of this framework should rarely require one to refer to this type, but this type will appear in inferred types. When writing type signatures the effect reader type abbreviation er
should be preferred.
type ('R, 'e, 'a, 'D) er = 'D -> ('R, 'e, 'a) s
Effect reader takes a dictionary 'D
of capabilities and returns an effect with the signature ('R, 'e, 'a) s
.
Effect readers are functions that take a dictionary of capabilities. This allows a form of laziness via η-expansion and effect signatures are designed to allow implementations to delay invoking the effect reader functions until the effects are really needed.
eta'0 @@ fun () -> body
is equivalent to fun d -> body d
.
Consider the following fib
implementation:
let rec fib n = eta'0 @@ fun () ->
if n <= 1 then
pure n
else
lift'2 (+) (fib (n - 2)) (fib (n - 1))
The eta'0 @@ fun () -> ...
makes it so that fib n
returns in O(1) time without building the complete computation tree.
eta'1 fn
is equivalent to fun x d -> fn x d
.
Consider the following list traversal implementation:
let rec map_er xyE = eta'1 @@ function
| [] -> pure []
| x :: xs ->
let+ y = xyE x
and+ ys = map_er xyE xs in
y :: ys
The eta'1 @@ function ...
makes it so that map_er xyE xs
returns in O(1) time without going through the whole list to compute a complete computation tree for it.
eta'2 fn
is equivalent to fun x y d -> fn x y d
.
map
effect signature.
map xy xE
effect.
( let+ ) xE xy
is equivalent to map xy xE
.
xE >>- xy
is equivalent to map xy xE
.
xyE >-> yz
is equivalent to fun x -> map yz (xyE x)
.
lift'1 xy xE
is equivalent to map xy xE
.
class virtual ['R, 'D] pointed' : object ... end
class ['R, 'O, 'D] pointed'of : ['R, 'O] pointed' -> object ... end
pure'0 (fun () -> exp)
is equivalent to eta'0 (fun () -> pure exp)
.
pure'1 fn x1
is equivalent to eta'0 (fun () -> pure (fn x1))
.
pure'2 fn x1 x2 x3
is equivalent to eta'0 (fun () -> pure (fn x1 x2))
.
val pure'3 :
('b1 -> 'b2 -> 'b3 -> 'a) ->
'b1 ->
'b2 ->
'b3 ->
('R, 'e, 'a, ['R, 'D] pure' as 'D) er
pure'3 fn x1 x2 x3
is equivalent to eta'0 (fun () -> pure (fn x1 x2 x3))
.
val pure'4 :
('b1 -> 'b2 -> 'b3 -> 'b4 -> 'a) ->
'b1 ->
'b2 ->
'b3 ->
'b4 ->
('R, 'e, 'a, ['R, 'D] pure' as 'D) er
pure'4 fn x1 x2 x3 x4
is equivalent to eta'0 (fun () -> pure (fn x1 x2 x3 x4))
.
do_unless b uE
is equivalent to if b then unit else uE
.
do_when b uE
is equivalent to if b then uE else unit
.
type ('R, 'e, 'a, 'b, 'D) pair'm =
('R, 'e, 'a, 'D) er ->
('R, 'e, 'b, 'D) er ->
('R, 'e, 'a * 'b) s
pair
effect signature.
class virtual ['R, 'D] product' : object ... end
class ['R, 'O, 'D] product'of : ['R, 'O] product' -> object ... end
class virtual ['R, 'D] applicative' : object ... end
class ['R, 'O, 'D] applicative'of : ['R, 'O] applicative' -> object ... end
pair xE yE
effect.
( and+ ) xE yE
is equivalent to pair xE yE
.
val (<*>) :
('R, 'e, 'a, ['R, 'D] pair' as 'D) er ->
('R, 'e, 'b, 'D) er ->
('R, 'e, 'a * 'b, 'D) er
xE <*> yE
is equivalent to pair xE yE
.
val tuple'2 :
('R, 'e, 'a, ['R, 'D] pair' as 'D) er ->
('R, 'e, 'b, 'D) er ->
('R, 'e, 'a * 'b, 'D) er
tuple'2 x1E x2E
is equivalent to pair x1E x2E
.
val tuple'3 :
('R, 'e, 'a1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'a2, 'D) er ->
('R, 'e, 'a3, 'D) er ->
('R, 'e, 'a1 * 'a2 * 'a3, 'D) er
tuple'3 x1E x2E x3E
is equivalent to map (fun (x1, (x2, x3)) -> (x1, x2, x3)) (pair x1E (pair x2E x3E))
.
val tuple'4 :
('R, 'e, 'a1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'a2, 'D) er ->
('R, 'e, 'a3, 'D) er ->
('R, 'e, 'a4, 'D) er ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4, 'D) er
tuple'4 x1E x2E x3E x4E
is like tuple'3
, but for 4 elements.
val tuple'5 :
('R, 'e, 'a1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'a2, 'D) er ->
('R, 'e, 'a3, 'D) er ->
('R, 'e, 'a4, 'D) er ->
('R, 'e, 'a5, 'D) er ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5, 'D) er
tuple'5 x1E x2E x3E x4E x5E
is like tuple'3
, but for 5 elements.
val tuple'6 :
('R, 'e, 'a1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'a2, 'D) er ->
('R, 'e, 'a3, 'D) er ->
('R, 'e, 'a4, 'D) er ->
('R, 'e, 'a5, 'D) er ->
('R, 'e, 'a6, 'D) er ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5 * 'a6, 'D) er
tuple'6 x1E x2E x3E x4E x5E x6E
is like tuple'3
, but for 6 elements.
val map_er'2 :
('b1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('b2 -> ('R, 'e, 'a2, 'D) er) ->
('b1 * 'b2) ->
('R, 'e, 'a1 * 'a2, 'D) er
2-tuple traversal.
val map_er'3 :
('b1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('b2 -> ('R, 'e, 'a2, 'D) er) ->
('b3 -> ('R, 'e, 'a3, 'D) er) ->
('b1 * 'b2 * 'b3) ->
('R, 'e, 'a1 * 'a2 * 'a3, 'D) er
3-tuple traversal.
val map_er'4 :
('b1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('b2 -> ('R, 'e, 'a2, 'D) er) ->
('b3 -> ('R, 'e, 'a3, 'D) er) ->
('b4 -> ('R, 'e, 'a4, 'D) er) ->
('b1 * 'b2 * 'b3 * 'b4) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4, 'D) er
4-tuple traversal.
val map_er'5 :
('b1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('b2 -> ('R, 'e, 'a2, 'D) er) ->
('b3 -> ('R, 'e, 'a3, 'D) er) ->
('b4 -> ('R, 'e, 'a4, 'D) er) ->
('b5 -> ('R, 'e, 'a5, 'D) er) ->
('b1 * 'b2 * 'b3 * 'b4 * 'b5) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5, 'D) er
5-tuple traversal.
val map_er'6 :
('b1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('b2 -> ('R, 'e, 'a2, 'D) er) ->
('b3 -> ('R, 'e, 'a3, 'D) er) ->
('b4 -> ('R, 'e, 'a4, 'D) er) ->
('b5 -> ('R, 'e, 'a5, 'D) er) ->
('b6 -> ('R, 'e, 'a6, 'D) er) ->
('b1 * 'b2 * 'b3 * 'b4 * 'b5 * 'b6) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5 * 'a6, 'D) er
6-tuple traversal.
val map_eq_er'2 :
('a1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('a2 -> ('R, 'e, 'a2, 'D) er) ->
('a1 * 'a2) ->
('R, 'e, 'a1 * 'a2, 'D) er
Physical equality preserving 2-tuple traversal.
val map_eq_er'3 :
('a1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('a2 -> ('R, 'e, 'a2, 'D) er) ->
('a3 -> ('R, 'e, 'a3, 'D) er) ->
('a1 * 'a2 * 'a3) ->
('R, 'e, 'a1 * 'a2 * 'a3, 'D) er
Physical equality preserving 3-tuple traversal.
val map_eq_er'4 :
('a1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('a2 -> ('R, 'e, 'a2, 'D) er) ->
('a3 -> ('R, 'e, 'a3, 'D) er) ->
('a4 -> ('R, 'e, 'a4, 'D) er) ->
('a1 * 'a2 * 'a3 * 'a4) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4, 'D) er
Physical equality preserving 4-tuple traversal.
val map_eq_er'5 :
('a1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('a2 -> ('R, 'e, 'a2, 'D) er) ->
('a3 -> ('R, 'e, 'a3, 'D) er) ->
('a4 -> ('R, 'e, 'a4, 'D) er) ->
('a5 -> ('R, 'e, 'a5, 'D) er) ->
('a1 * 'a2 * 'a3 * 'a4 * 'a5) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5, 'D) er
Physical equality preserving 5-tuple traversal.
val map_eq_er'6 :
('a1 -> ('R, 'e, 'a1, ['R, 'D] product' as 'D) er) ->
('a2 -> ('R, 'e, 'a2, 'D) er) ->
('a3 -> ('R, 'e, 'a3, 'D) er) ->
('a4 -> ('R, 'e, 'a4, 'D) er) ->
('a5 -> ('R, 'e, 'a5, 'D) er) ->
('a6 -> ('R, 'e, 'a6, 'D) er) ->
('a1 * 'a2 * 'a3 * 'a4 * 'a5 * 'a6) ->
('R, 'e, 'a1 * 'a2 * 'a3 * 'a4 * 'a5 * 'a6, 'D) er
Physical equality preserving 6-tuple traversal.
val lift'2 :
('b1 -> 'b2 -> 'a) ->
('R, 'e, 'b1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'b2, 'D) er ->
('R, 'e, 'a, 'D) er
lift'2 xyz xE yE
is equivalent to map (fun (x, y) -> xyz x y) (pair xE yE)
.
val lift'3 :
('b1 -> 'b2 -> 'b3 -> 'a) ->
('R, 'e, 'b1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'b2, 'D) er ->
('R, 'e, 'b3, 'D) er ->
('R, 'e, 'a, 'D) er
Lift 3-parameter function to operate on effect readers.
val lift'4 :
('b1 -> 'b2 -> 'b3 -> 'b4 -> 'a) ->
('R, 'e, 'b1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'b2, 'D) er ->
('R, 'e, 'b3, 'D) er ->
('R, 'e, 'b4, 'D) er ->
('R, 'e, 'a, 'D) er
Lift 4-parameter function to operate on effect readers.
val lift'5 :
('b1 -> 'b2 -> 'b3 -> 'b4 -> 'b5 -> 'a) ->
('R, 'e, 'b1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'b2, 'D) er ->
('R, 'e, 'b3, 'D) er ->
('R, 'e, 'b4, 'D) er ->
('R, 'e, 'b5, 'D) er ->
('R, 'e, 'a, 'D) er
Lift 5-parameter function to operate on effect readers.
val lift'6 :
('b1 -> 'b2 -> 'b3 -> 'b4 -> 'b5 -> 'b6 -> 'a) ->
('R, 'e, 'b1, ['R, 'D] product' as 'D) er ->
('R, 'e, 'b2, 'D) er ->
('R, 'e, 'b3, 'D) er ->
('R, 'e, 'b4, 'D) er ->
('R, 'e, 'b5, 'D) er ->
('R, 'e, 'b6, 'D) er ->
('R, 'e, 'a, 'D) er
Lift 6-parameter function to operate on effect readers.
type ('R, 'e, 'a, 'b, 'c, 'D) branch'm =
('R, 'e, 'b -> 'a, 'D) er ->
('R, 'e, 'c -> 'a, 'D) er ->
('R, 'e, ('b, 'c) branch, 'D) er ->
('R, 'e, 'a) s
branch
effect signature.
class virtual ['R, 'D] selective' : object ... end
class ['R, 'O, 'D] selective'of : ['R, 'O] selective' -> object ... end
val branch :
('R, 'e, 'b -> 'a, 'D) er ->
('R, 'e, 'c -> 'a, 'D) er ->
('R, 'e, ('b, 'c) branch, ['R, 'D] branch' as 'D) er ->
('R, 'e, 'a, 'D) er
branch baE caE bcE
effect.
val if_else_s :
('R, 'e, 'a, 'D) er ->
('R, 'e, 'a, 'D) er ->
('R, 'e, bool, ['R, 'D] selective' as 'D) er ->
('R, 'e, 'a, 'D) er
if_else_s tE eE cE
is equivalent to
branch
(map const eE)
(map const tE)
(map (function true -> `Fst ()
| false -> `Snd ())
cE)
type ('R, 'e, 'a, 'b, 'D) bind'm =
('R, 'e, 'b, 'D) er ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'e, 'a) s
bind
effect signature.
class virtual ['R, 'D] monad' : object ... end
class virtual ['R, 'D] monad'd : object ... end
val bind :
('R, 'e, 'b, ['R, 'D] bind' as 'D) er ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'e, 'a, 'D) er
bind xE xyE
effect.
val (>>=) :
('R, 'e, 'b, ['R, 'D] bind' as 'D) er ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'e, 'a, 'D) er
xE >>= xyE
is equivalent to bind xE xyE
.
val let* :
('R, 'e, 'b, ['R, 'D] bind' as 'D) er ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'e, 'a, 'D) er
( let* ) xE xyE
is equivalent to bind xyE xE
.
( and* ) xE yE
is equivalent to pair xE yE
.
join xEE
is equivalent to bind xEE (fun xE -> xE)
.
uE >> xE
is equivalent to bind uE (fun () -> xE)
.
val (>=>) :
('a -> ('R, 'e, 'b, ['R, 'D] bind' as 'D) er) ->
('b -> ('R, 'e, 'c, 'D) er) ->
'a ->
('R, 'e, 'c, 'D) er
xyE >=> yzE
is equivalent to fun x -> bind (xyE x) yzE
.
lE ||| rE
is equivalent to bind lE (function true -> pure true | false -> rE)
.
lE &&& rE
is equivalent to bind lE (function true -> rE | false -> pure true)
.
alt
effect signature.
iota n
is equivalent to zero <|> pure 0 <|> ... <|> pure (n-1)
.
val filter :
('a -> bool) ->
('R, 'e, 'a, < ('R, 'D) monad' ; ('R, 'D) zero'.. > as 'D) er ->
('R, 'e, 'a, 'D) er
filter p
type ('R, 'e, 'f, 'a, 'b, 'D) tryin'm =
('f -> ('R, 'e, 'a, 'D) er) ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'f, 'b, 'D) er ->
('R, 'e, 'a) s
tryin
effect signature.
val tryin :
('f -> ('R, 'e, 'a, ['R, 'D] tryin' as 'D) er) ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'f, 'b, 'D) er ->
('R, 'e, 'a, 'D) er
tryin exE yxE xE
effect.
val catch :
('R, 'e, 'a, < ('R, 'D) pure' ; ('R, 'D) tryin'.. > as 'D) er ->
('R, 'f, ('e, 'a) res, 'D) er
catch xE
is equivalent to tryIn (fun e -> pure (`Error e)) (fun x -> pure (`Ok x)) xE
.
val handle :
('f -> ('R, 'e, 'a, < ('R, 'D) pure' ; ('R, 'D) tryin'.. > as 'D) er) ->
('R, 'f, 'a, 'D) er ->
('R, 'e, 'a, 'D) er
handle exE xE
is equivalent to tryin exE pure xE
val finally :
('R, 'e, unit, < ('R, 'D) monad' ; ('R, 'D) errors'.. > as 'D) er ->
('R, 'e, 'a, 'D) er op'1
finally uE xE
is equivalent to tryin (fun e -> bind uE (fun () -> fail e)) (fun x -> bind uE (fun () -> pure x)) xE
.
val map_error :
('f -> 'e) ->
('R, 'f, 'a, < ('R, 'D) pure' ; ('R, 'D) errors'.. > as 'D) er ->
('R, 'e, 'a, 'D) er
map_error fe xE
is equivalent to tryin (fun f -> fail (fe e)) pure xE
.
val gen_error :
('R, nothing, 'a, < ('R, 'D) pure' ; ('R, 'D) errors'.. > as 'D) er ->
('R, 'e, 'a, 'D) er
gen_error xE
is equivalent to map_error (function (_: nothing) -> .) xE
.
par xE yE
effect.
( let&+ ) xE xy
is equivalent to map xy xE
.
( and&+ ) xE yE
is equivalent to par xE yE
.
val let&* :
('R, 'e, 'b, ['R, 'D] bind' as 'D) er ->
('b -> ('R, 'e, 'a, 'D) er) ->
('R, 'e, 'a, 'D) er
( let&* ) xE xyE
is equivalent to bind xyE xE
.
( and&* ) xE yE
is equivalent to par xE yE
.
suspend
effect signature.
class ['R, 'O, 'D] suspend'of : ['R, 'O] suspend' -> object ... end
suspend
capability projection.
suspend with_resume
effect.
class virtual ['R, 'D] async' : object ... end
module Memo : sig ... end
Memoized lazy computation for asynchronous programming.
module Mut : sig ... end
Mutable ref cells for asynchronous programming.
The environment or reader is built-in to the effect system in the form of the capability dictionary. Thanks to OCaml's structural objects, it is possible to extend the dictionary for user needs.
env
effect returns the dictionary 'd
of capabilities.
env_as fn
effect returns the value of type 'a
computed from the dictionary of capabilities of type 'd
.
mapping_env fn er
effect executes the effect er
with the dictionary of capabilities of type 'D
mapped through the given function fn
.
setting_env s er
effect executes the effect er
with the given dictionary s
of capabilities.
module Prop : sig ... end
An abstraction for accessing instance variables or properties of objects.
val prop : (unit -> 'a) -> ('a -> unit) -> 'a Prop.t
prop get set
is equivalent to Prop.make get set
.
get prop_of
effect returns the value of the property from the dictionary of capabilities of type 'd
.
get_as prop_of fn
mapping prop_of fn xE
effect executes the effect xE
with the property of the dictionary of capabilities mapped through the given function.
setting prop_of value xE
effect executess the effect xE
with the property of the dictionary of capabilities set to given value
.
val read :
('D -> 'v Mut.t Prop.t) ->
('R, 'e, 'v, < ('R, 'D) monad' ; ('R, 'D) suspend'.. > as 'D) er
read prop_of
effect.
val mutate :
('D -> 'v Mut.t Prop.t) ->
'v op'1 ->
('R, 'e, unit, < ('R, 'D) monad' ; ('R, 'D) suspend'.. > as 'D) er
mutate prop_of vv
effect.
val modify :
('D -> 'v Mut.t Prop.t) ->
('v -> 'v * 'a) ->
('R, 'e, 'a, < ('R, 'D) monad' ; ('R, 'D) suspend'.. > as 'D) er
modify prop_of vva
effect.
val try_mutate :
('D -> 'v Mut.t Prop.t) ->
('v -> ('R, 'e, 'v, 'D) er) ->
('R,
'e,
unit,
< ('R, 'D) monad'
; ('R, 'D) errors'
; ('R, 'D) suspend'.. > as 'D)
er
try_mutate prop_of vvE
effect.
val try_modify :
('D -> 'v Mut.t Prop.t) ->
('v -> ('R, 'e, 'v * 'a, 'D) er) ->
('R,
'e,
'a,
< ('R, 'D) monad'
; ('R, 'D) errors'
; ('R, 'D) suspend'.. > as 'D)
er
try_modify prop_of vvaE
effect.
val cloning :
('D -> 'v Mut.t Prop.t) ->
('R, 'e, 'a, < ('R, 'D) monad' ; ('R, 'D) suspend'.. > as 'D) er op'1
cloning prop_of xE
effect.
Users can and often should implement their own interpreters that only allow specific limited sets of effects. This package is intended to only provide the core framework for effectul programming.
module StdRea : sig ... end
Stdlib
extension modules.
module Constant : sig ... end
Constant functor, products, and applicatives.
module Identity : sig ... end
Identity monad.
module Tailrec : sig ... end
A self tail recursive interpreter usable with Js_of_ocaml.
module Traverse : sig ... end
Data type traversal use cases.