123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126(** A minimal implementation of the Promise monad.
It is an answer to an exercise question in
{{:https://courses.cs.cornell.edu/cs3110/2021sp/textbook/adv/promises.ml}CS
3110 - Functional Programming in OCaml - 12.7 Exercises}*)modulePromiseMinimal:sigincludeBuildConstraints.MONAD_PROMISEincludeBuildConstraints.PROMISE_IMPLwithtype'apromise='atvalrun_promise:'at->'aend=structtyperesolved_promise_state=unittype'adetailed_state=Pending|Resolvedof'a|Rejectedofexntype'ahandler='adetailed_state->unit(** RI: the input may not be [Pending] *)type'at={mutablestate:'adetailed_state;mutablehandlers:'ahandlerlist;}(** RI: if [state <> Pending] then [handlers = []]. *)type'apromise='atletenqueue(handler:'adetailed_state->unit)(promise:'at):unit=promise.handlers<-handler::promise.handlerstype'aresolver='at(** [write_once p s] changes the state of [p] to be [s]. If [p] and [s] are
both pending, that has no effect. Raises: [Invalid_arg] if the state of
[p] is not pending. *)letwrite_onceps=ifp.state=Pendingthenp.state<-selseinvalid_arg"cannot write twice"letmake_promise()=letp={state=Pending;handlers=[]}in(p,p)letreturnx={state=Resolvedx;handlers=[]}letpure=returnletreturn_promise=returnletpromise_state(typea)({state;handlers=_}:at):resolved_promise_stateBuildConstraints.universal_promise_state=matchstatewith|Pending->Pending|Resolved_->Resolved()|Rejectedexn->Rejectedexn(** requires: [st] may not be [Pending] *)letresolve_or_reject(r:'aresolver)(st:'adetailed_state)=assert(st<>Pending);lethandlers=r.handlersinr.handlers<-[];write_oncerst;List.iter(funf->fst)handlersletrejectrx=resolve_or_rejectr(Rejectedx)letresolverx=resolve_or_rejectr(Resolvedx)lethandler(resolver:'aresolver):'ahandler=function|Pending->failwith"handler RI violated"|Rejectedexc->rejectresolverexc|Resolvedx->resolveresolverxlethandler_of_callback(callback:'a->'bt)(resolver:'bresolver):'ahandler=function|Pending->failwith"handler RI violated"|Rejectedexc->rejectresolverexc|Resolvedx->(letpromise=callbackxinmatchpromise.statewith|Resolvedy->resolveresolvery|Rejectedexc->rejectresolverexc|Pending->enqueue(handlerresolver)promise)letbind(typea)(typeb)(input_promise:at)(callback:a->bt):bt=matchinput_promise.statewith|Resolvedx->callbackx|Rejectedexc->{state=Rejectedexc;handlers=[]}|Pending->letoutput_promise,output_resolver=make_promise()inenqueue(handler_of_callbackcallbackoutput_resolver)input_promise;output_promiseletbind_promise=bindletmapfxs=bindxs(funx->pure(fx))letapply(typea)(typeb)(f:(a->b)t)(x:at):bt=bindf(funy->mapyx)(** This implemnetation runs sequentially. *)letparallelps=letrecauxacc=function|[]->return(List.revacc)|p::ps'->bindp(funx->letacc'=x::accinauxacc'ps')inaux[]psletrun_promise(typea)(x:at):a=matchxwith|{state=Resolveda;_}->(* Promise is already resolved. *)a|{state=Rejectede;_}->(* TODO: Log the exception? *)raisee|{state=Pending;_}->((* In Pending. Trigger the promise resolution cascade ... *)letresult=refNoneinletunit'=map(funa->result:=Somea)xinresolveunit'();(* Check if the resolution was rejected *)match(unit',!result)with|{state=Resolved();_},Somea->a|{state=Resolved();_},None->failwith"promised handler violation: missed promise resolution"|{state=Rejectede;_},_->raisee|{state=Pending;_},_->failwith"promised handler violation: resolution still pending")end