123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106(*---------------------------------------------------------------------------
Copyright (c) 2020 The brr programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)(* We represent futures by an object { fut : <promise> } with a single
[fut] JavaScript Promise object which, by construction, *never*
rejects. The promise is wrapped in an object because JavaScript's
[resolve] which should be monadic [return] unfortunately also
monadically [join]s. This JavaScript expression:
Promise.resolve (Promise.resolve ("Noooooo!"))
evaluates to: Promise {<fulfilled>: "Noooooo!"}
instead of: Promise {<fulfilled>: Promise {<fulfilled>: ""Noooooo!""}}
This makes it impossible to type [resolve] correctly in OCaml since it
would need to have these two types:
val resolve : 'a -> 'a Promise.t
val resolve : 'a Promise.t -> 'a Promise.t
In general this breaks type safety for example [bind]ing a ['a
Fut.t Fut.t] value your function could end up with a ground value
of type ['a] not the expected ['a Fut.t] value as argument. By
wrapping the promise in an object we can control that. *)type'at=Jv.t(* a JavaScript object of the form: { fut : <promise> } *)letfutp=Jv.obj[|"fut",p|]letpromisef=Jv.getf"fut"letpromise'f=Jv.getf"fut"letcreate()=(* Ugly as shit but that's what new Promise gives us. *)letnot_set=fun_->assertfalseinletis_set=fun_->Jv.throw(Jstr.v"The future is already set")inletsetter=refnot_setinletset_setterresolve_reject=setter:=resolveinletp=Jv.Promise.createset_setterinletsetv=!setterv;setter:=is_setinfutp,setletawaitfk=Jv.Promise.await(promisef)kletreturnv=fut@@Jv.Promise.resolvevletbindffn=fut@@Jv.Promise.bind(promisef)(funv->promise(fnv))letmapfnf=bindf(funv->return(fnv))letpairf0f1=fut@@Jv.Promise.bind(promisef0)@@funv0->Jv.Promise.bind(promisef1)@@funv1->Jv.Promise.resolve(v0,v1)letof_listfs=letarr=Jv.of_listpromise'fsinletall=Jv.Promise.allarrinletto_listl=Jv.Promise.resolve(Jv.to_listObj.magicl)infut@@Jv.Promise.bindallto_listlettick~ms=fut@@Jv.Promise.create@@funres_rej->ignore(Jv.apply(Jv.getJv.global"setTimeout")Jv.[|callback~arity:1res;of_intms|])(* Converting with JavaScript promises *)typenonrec('a,'b)result=('a,'b)resultttype'aor_error=('a,Jv.Error.t)resultletokv=return(Okv)leterrore=return(Errore)letof_promise'~ok~errorp=letokv=Jv.Promise.resolve(Ok(okv))inleterrore=Jv.Promise.resolve(Error(errore))infut@@Jv.Promise.then'pokerrorletto_promise'~ok~errorf=Jv.Promise.create@@funresrej->awaitf@@function|Okv->res(okv)|Errore->rej(errore)letof_promise~okv=of_promise'~ok~error:Jv.to_errorvletto_promise~okv=to_promise'~ok~error:Jv.of_errorv(* Future syntaxes *)moduleSyntax=structlet(let*)=bindlet(and*)=pairlet(let+)ffn=mapfnflet(and+)=(and*)endmoduleResult_syntax=structletresult_pairr=matchrwith|(Error_asr),_|_,(Error_asr)->r|Okv0,Okv1->Ok(v0,v1)let(let*)ffn=bindf@@function|Okv->fnv|Error_ase->returnelet(and*)f0f1=mapresult_pair(pairf0f1)let(let+)ffn=map(Result.mapfn)flet(and+)=(and*)end