123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126(** A monad representing computation that can be cooperatively scheduled.
Computations can yield, and fork (Choice). *)moduleSchedulable=structtype('a,'prio)t=|Prune|Nowof'a|Yieldof'prio*(unit->('a,'prio)t)|Choiceof('a,'prio)t*('a,'prio)tlet[@inline]return(x:'a):('a,_)t=Nowxletrecbind(x:('a,'prio)t)(f:'a->('b,'prio)t):('b,'prio)t=matchxwith|Prune->Prune|Nowv->fv|Yield(prio,step)->Yield(prio,fun()->bind(step())f)|Choice(a,b)->Choice(bindaf,bindbf)let[@inline]map(f:'a->'b)(x:('a,'prio)t):('b,'prio)t=bindx(funx->return(fx))let[@inline]yield(prio:'prio):(unit,'prio)t=Yield(prio,Fun.const(Now()))let[@inline]choose(x:('a,'prio)t)(y:('a,'prio)t):('a,'prio)t=Choice(x,y)letprune:(_,_)t=Pruneend(* Add a notion of State to the Schedulable monad. "Transformer without module functor" style. *)moduleState=structtype('a,'prio,'state)t='state->('a*'state,'prio)Schedulable.tlet[@inline]return(x:'a):('a,'prio,'state)t=fun(state:'state)->Schedulable.return(x,state)let[@inline]bind(x:('a,'prio,'state)t)(f:'a->('b,'prio,'state)t):('b,'prio,'state)t=fun(state:'state)->Schedulable.bind(xstate)(fun(x,state)->fxstate)let[@inline]map(f:'a->'b)(x:('a,'prio,'state)t):('b,'prio,'state)t=bindx(funx->return(fx))let[@inline]lift(x:('a,'prio)Schedulable.t):('a,'prio','state)t=fun(state:'state)->Schedulable.map(funx->(x,state))xlet[@inline]lift2(f:'x->'y->('a*'state,'prio)Schedulable.t)(x:'state->'x)(y:'state->'y):('a,'prio,'state)t=funstate->f(xstate)(ystate)let[@inline]with_state(f:'state->'a*'state):('a,'prio,'state)t=funstate->Schedulable.return(fstate)let[@inline]modify_state(f:'state->'state):(unit,'prio,'state)t=funstate->Schedulable.return((),fstate)end(* Add a notion of faillibility to the evaluation. "Transformer without module functor" style. *)type('a,'err,'prio,'state)t=(('a,'err)result,'prio,'state)State.tlet[@inline]return(x:'a):('a,'err,'prio,'state)t=State.return(Okx)let[@inline]lift(x:('a,'prio,'state)State.t):('a,'err,'prio,'state)t=State.mapResult.okxlet[@inline]bind(x:('a,'err,'prio,'state)t)(f:'a->('b,'err,'prio,'state)t):('b,'err,'prio,'state)t=State.bindx(functionOkx->fx|Error_ase->State.returne)let[@inline](let*)(x:('a,'err,'prio,'state)t)f:_t=bindxflet[@inline]map(f:'a->'b)(x:('a,'err,'prio,'state)t):('b,'err,'prio,'state)t=State.map(funx->Result.mapfx)xlet[@inline](let+)(x:('a,'err,'prio,'state)t)(f:'a->'b):('b,'err,'prio,'state)t=mapfxlet[@inline]lift_schedulable(v:('a,'prio)Schedulable.t):('a,'err,'prio,'state)t=letv=State.liftvinliftvlet[@inline]with_state(f:'state->'a):('a,'err,'prio,'state)t=letx=State.with_state(funstate->(fstate,state))inliftxlet[@inline]state():('state,'err,'prio,'state)t=with_stateFun.idlet[@inline]modify_state(f:'state->'state):(unit,'err,'prio,'state)t=lift(State.modify_statef)let[@inline]set_state(state:'state):(unit,'err,'prio,'state)t=modify_state(Fun.conststate)(* Create two new branches, they do not yield so the yield should be created manually! *)let[@inline]choose(x:('a,'err,'prio,'state)t)(y:('a,'err,'prio,'state)t):('a,'err,'prio,'state)t=State.lift2Schedulable.choosexy(* Yield the current branch (i.e. add it to the work queue so that it gets executed later. )*)let[@inline]yield(prio:'prio):(unit,'err,'prio,'state)t=lift_schedulable@@Schedulable.yieldprio(* Child will be a new branch that immediately yields, and parent will execute directly without yielding. *)let[@inline]fork~(parent:('a,'err,'prio,'state)t)~(child:'prio*('a,'err,'prio,'state)t):('a,'err,'prio,'state)t=letprio,child=childinletchild=bind(yieldprio)(fun()->child)inchooseparentchildlet[@inline]prune():('a,'err,'prio,'state)t=lift_schedulableSchedulable.prunelet[@inline]fail(err:'err):('a,'err,'prio,'state)t=State.return(Errorerr)letrun(f:('a,'err,'prio,'state)t)(state:'state):(('a,'err)result*'state,'prio)Schedulable.t=fstate