This is probably not very useful in practice, but I thought that some people may appreciate seeing this. Similar to how effects can create a generator out of any iter
function, they can also create an await
out of any monad.
module type MONAD = sig
type 'a t
val return : 'a -> 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
end
module MakeAwait (M : MONAD) = struct
type _ Effect.t += Await : 'a M.t -> 'a Effect.t
let await m = Effect.perform (Await m)
let run f =
match f () with
| x -> x
| effect Await m, k -> M.bind m (Effect.Deep.continue k)
end
I’m not sure if this is generally advisable over the tried-and-true let*
syntax, but is still a fun trick. Its benefits and tradeoffs are different compared to let*
, so perhaps it fills a need for someone. Example usage:
module AResult = MakeAwait (struct
type 'a t = ('a, exn) Result.t
let return = Result.ok
let bind = Result.bind
end)
let hello_world =
AResult.run (fun () ->
let s1 = AResult.await (Ok "hello") in
let s2 = AResult.await (Ok "world") in
Ok (s1 ^ s2))
The only time I’ve felt a desire to use a pattern like this was when writing JSOO code that interfaced with external JavaScript promises, and I wanted to enjoy JS-style await. (Yes, I know JS promises themselves are not type-safe.)
open Js_of_ocaml
type 'a promise
type _ Effect.t += Await : 'a promise Js.t -> 'a Effect.t
let await p = Effect.perform (Await p)
let run f =
match f () with
| x -> Js.Unsafe.global##._Promise##resolve x
| exception x -> Js.Unsafe.global##._Promise##reject x
| effect Await p, k ->
Js.Unsafe.meth_call p "then"
[|
Js.Unsafe.inject (Js.wrap_callback (Effect.Deep.continue k));
Js.Unsafe.inject (Js.wrap_callback (Effect.Deep.discontinue k));
|]