123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439(*----------------------------------------------------------------------------
* Copyright (c) 2020, António Nuno Monteiro
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*---------------------------------------------------------------------------*)includeArchi_intfmoduleMake(Io:IO)=structmoduleIo=structincludeIomoduleInfix=structlet(>|=)xf=mapfxendmoduleResult=structletreturnx=Io.return(Okx)letbindxf=bindx(functionOkx->fx|Error_aserr->Io.returnerr)letmapfx=map(functionOkx->Ok(fx)|Error_aserr->err)xmoduleInfix=structlet(>|=)xf=mapfxlet(>>=)=bindendendendmoduleTypes=structmodulerecComponent:sigtype(_,_,_)deps=|[]:('ctx,'ty,'ty)deps|(::):('ctx,'a)t*('ctx,'b,'ty)deps->('ctx,'a->'b,'ty)depsand(_,_)t=|Component:{dependencies:('ctx,'args,('ty,[`Msgofstring])resultIo.t)deps;start:'ctx->'args;stop:'ty->unitIo.t;hkey:'tyHmap.key}->('ctx,'ty)t|System:('ctx,'args,'ty)System.system->('ctx,'ty)ttype_any_component=AnyComponent:('ctx,_)t->'ctxany_componentend=ComponentandSystem:sigtype(_,_,_)components=|[]:('ctx,'ty,'ty)components|(::):(string*('ctx,'a)Component.t)*('ctx,'b,'ty)components->('ctx,'a->'b,'ty)componentstype('ctx,'args,'ty)system={components:('ctx,'args,'ty)components;hkey:'tyHmap.key;lift:'args}type(_,_,_)t=|System:{system:('ctx,'args,'ty)system;values:Hmap.t}->('ctx,'ty,_)tvalto_any_component_list:('ctx,'args,'ty)system->'ctxComponent.any_componentlistend=structtype(_,_,_)components=|[]:('ctx,'ty,'ty)components|(::):(string*('ctx,'a)Component.t)*('ctx,'b,'ty)components->('ctx,'a->'b,'ty)componentstype('ctx,'args,'ty)system={components:('ctx,'args,'ty)components;hkey:'tyHmap.key;lift:'args}type(_,_,_)t=|System:{system:('ctx,'args,'ty)system;values:Hmap.t}->('ctx,'ty,_)tletfold_left~f~init{components;_}=letrecloop:typetyargs.f:('res->string*'ctxComponent.any_component->'res)->init:'res->('ctx,args,ty)components->'res=fun~f~initdeps->matchdepswith|[]->init|(lbl,x)::xs->loop~f~init:(finit(lbl,AnyComponentx))xsinloop~f~initcomponentsletto_any_component_listsystem=fold_left~f:(fun(acc:'ctxComponent.any_componentlist)(_lbl,itm)->itm::acc)~init:[]systemendendmoduleComponent=structincludeTypes.ComponentmoduleSystem=Types.SystemmoduletypeCOMPONENT=sigtypettypectxtypeargsvalstart:ctx->argsvalstop:t->unitIo.tendmoduletypeSIMPLE_COMPONENT=sigtypetincludeCOMPONENTwithtypet:=tandtypeargs:=(t,[`Msgofstring])resultIo.tendletfold_left~f~initdependencies=letrecloop:typetyargs.f:('res->'ctxany_component->'res)->init:'res->('ctx,args,ty)deps->'res=fun~f~initdeps->matchdepswith|[]->init|x::xs->loop~f~init:(finit(AnyComponentx))xsinloop~f~initdependenciesletappend:typetyab.('ctx,a)t->('ctx,b,ty)deps->('ctx,a->b,ty)deps=funcdeps->matchdepswith[]->[c]|xs->c::xsletrecconcat:typetyab.('ctx,a,ty)deps->('ctx,ty,b)deps->('ctx,a,b)deps=fund1d2->matchd1with[]->d2|x::xs->x::concatxsd2letmake:typectxty.start:(ctx->(ty,[`Msgofstring])resultIo.t)->stop:(ty->unitIo.t)->(ctx,ty)t=fun~start~stop->Component{start;stop;hkey=Hmap.Key.create();dependencies=[]}letidentity:typectxty.ty->(ctx,ty)t=func->letstart_ctx=Io.Result.returncinletstop_c=Io.return()inmake~start~stopletmake_m:typectxa.(moduleSIMPLE_COMPONENTwithtypet=aandtypectx=ctx)->(ctx,a)t=fun(moduleC)->Component{start=C.start;stop=C.stop;hkey=Hmap.Key.create();dependencies=[]}letusing:typectxtyargs.start:(ctx->args)->stop:(ty->unitIo.t)->dependencies:(ctx,args,(ty,[`Msgofstring])resultIo.t)deps->(ctx,ty)t=fun~start~stop~dependencies->Component{start;stop;hkey=Hmap.Key.create();dependencies}letusing_m:typectxtyargs.(moduleCOMPONENTwithtypet=tyandtypeargs=argsandtypectx=ctx)->dependencies:(ctx,args,(ty,[`Msgofstring])resultIo.t)deps->(ctx,ty)t=fun(moduleC)~dependencies->Component{start=C.start;stop=C.stop;hkey=Hmap.Key.create();dependencies}letof_system(System.System{system;_})=Systemsystemletrecequal:'ctxany_component->'ctxany_component->bool=fun(AnyComponentc1)(AnyComponentc2)->matchc1,c2with|System_,Component_|Component_,System_->false|Component{hkey=k1;_},Component{hkey=k2;_}->Hmap.Key.equal(Hmap.Key.hide_typek1)(Hmap.Key.hide_typek2)|Systems1,Systems2->List.for_all2(funxy->equalxy)(System.to_any_component_lists1)(System.to_any_component_lists2)end(** System *)moduleSystem=structincludeTypes.System(* Only here for switching the `started` / `stopped` phantom types. *)externalcast:('ctx,'ty,_)t->('ctx,'ty,_)t="%identity"letreclift_ignore:typectxargs.(ctx,args,unit)components->args=funcomponents->matchcomponentswith_::xs->fun_->lift_ignorexs|[]->()letmake_reusable:typeargsty.lift:args->('ctx,args,ty)components->('ctx,ty,[`stopped])t=fun~liftcomponents->System{system={components;hkey=Hmap.Key.create();lift};values=Hmap.empty}letmakecomponents=make_reusable~lift:(lift_ignorecomponents)componentsletrecsafe_fold~f~init(sorted_components:'ctxComponent.any_componentlist)=matchsorted_componentswith|[]->Io.Result.returninit|x::xs->letopenIo.Result.Infixinfinitx>>=funacc->safe_fold~init:acc~fxs(* This function assumes dependencies have been started. The usage of
* `Hmap.get`, even though it throws, is considered safe given that we have
* topologically sorted the component's dependencies. *)letrecstart_component:typetyargs.('ctx,_,[`stopped])t->dependencies:('ctx,args,(ty,[`Msgofstring])resultIo.t)Component.deps->f:args->(ty,[`Msgofstring])resultIo.t=fun(System{values;_}assystem)~dependencies~f->letopenComponentinmatchdependencieswith|[]->f|Component{hkey;_}::xs->letstarted_dep=Hmap.gethkeyvaluesinstart_componentsystem~dependencies:xs~f:(fstarted_dep)|System{hkey;_}::xs->letlifted_system=Hmap.gethkeyvaluesinstart_componentsystem~dependencies:xs~f:(flifted_system)letupdate_system~f~order(System{system;_}ast)=letall_components=to_any_component_listsystemintryletordered=Toposort.toposort~order~equal:Component.equal~edges:(fun_graph(Component.AnyComponentc)->matchcwith|Component.Component{dependencies;_}->Component.fold_left~f:(fun(acc:'ctxComponent.any_componentlist)itm->itm::acc)~init:[]dependencies|Component.Systemsystem'->letcomponents=to_any_component_listsystem'inList.fold_left(fun(acc:'ctxComponent.any_componentlist)itm->itm::acc)[]components)all_componentsinsafe_fold~init:t~forderedwith|Toposort.CycleFound->Io.return(Error`Cycle_found)letreclift_system:typetyargs.('ctx,_,_)t->components:('ctx,args,ty)components->f:args->ty=fun(System{values;_}assystem)~components~f->letopenComponentinmatchcomponentswith|[]->f|(_lbl,Component{hkey;_})::xs->letlifted_arg=Hmap.gethkeyvaluesinlift_systemsystem~components:xs~f:(flifted_arg)|(_lbl,System{hkey;_})::xs->letlifted_arg=Hmap.gethkeyvaluesinlift_systemsystem~components:xs~f:(flifted_arg)letstartctxsystem=letopenIo.Infixinletf(System({values;_}ass)assystem)(Component.AnyComponentc)=matchcwith|Component.Component{start;dependencies;hkey;_}->letf=startctxinstart_componentsystem~dependencies~f>|=(function|Okstarted_component->letvalues=Hmap.addhkeystarted_componentvaluesinOk(System{swithvalues})|Errore->Error(e:>[`Cycle_found|`Msgofstring]))|Component.System{components;hkey;lift;_}->(* A system is assumed to have all its components already started
* because of topological sorting.
*
* For systems we need to do 2 things:
* 1. Lift the system to a value;
* 2. Pass that value to `f`, the component `resolver`. *)letf=liftinletlifted_system=lift_systemsystem~components~finletvalues=Hmap.addhkeylifted_systemvaluesinIo.Result.return(System{swithvalues})inIo.Result.mapcast(update_system~order:`Dependency~fsystem)letstopsystem=letopenIo.Result.Infixinupdate_system~order:`Reverse~f:(fun(System({values;_}ass))(Component.AnyComponentc)->matchcwith|Component.Component{stop;hkey;_}->letopenIo.Infixinletv=Hmap.gethkeyvaluesinstopv>|=fun()->letvalues=Hmap.remhkeyvaluesinOk(System{swithvalues})|Component.System_->Io.Result.return(Systems))system>|=castletget(System{system={components;lift;_};_}ast)=lift_systemt~components~f:liftendendmoduleSync:IOwithtype+'at='a=structtype+'at='aletreturnx=xletmapfx=fxletbindxf=fxendincludeMake(Sync)