123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372moduleU=Univtype'awitness='aU.witnesstypeelt=U.binding(* Utility option monad functions *)let(|>?)xf=matchxwith|Somex->Some(fx)|None->Nonelet(>>?)xf=matchxwith|Somex->fx|None->()(* Key type :
* 'ty the type of the key
* 'tys the type of the stored value
* 'mut : storage brand either imm or mut
*)openType_datatype'datakey={witness:'tyswitness;storage:('ty,'tys,'m)storage;access:('ty,'tya)access}constraint'data=<mut:'m;typ:'ty;access:'tya;stored:'tys>moduletypeS=sigopenType_dataincludeBijection.S(** The type of record within the namespace *)typet(** The type of a field getter or updater *)type'infofield_action(** Aliases for the type of fields *)type'infoget=(('a,'mut)getter*'res)field_actionconstraint'info=<x:'a;mut:'mut;ret:'res>type'afield=<x:'a;mut:imm;ret:'aoption>gettype'amut_field=<x:'a;mut:mut;ret:'aoption>gettype'aexn_field=<x:'a;mut:imm;ret:'a>gettype'aexn_mut_field=<x:'a;mut:mut;ret:'a>gettype('param,'t)update=('paramupdater*'t)field_action(** The empty record *)valempty:t(** Create a new open record from a list of field updater :
[create [ field1 ^= value1; field2 ^= value2; ... ] ]
Only const updater make sense in this context,
since there is no fields present.
*)valcreate:(onlyconst,t)updatelist->t(** Creation of a new fields.
Note that the type 'ty would be weakly polymorphic once the field created.
However, in this specific use case, it seems reasonable to annotate the
field type by using one of the field type aliases.
*)valnew_field:unit->'tyfieldvalnew_field_mut:unit->'tymut_fieldvalnew_field_exn:unit->'tyexn_fieldvalnew_field_exn_mut:unit->'tyexn_mut_field(** Constant field updater:
[record.{ field ^= v }] sets the value of [field] to [v]
and is equivalent to [record.{ put field v }] *)valput:<x:'ty;..>get->'ty->(_const,t)updateval(^=):<x:'ty;..>get->'ty->(_const,t)update(** Field map:
[ record.{field |= f } ] or [record.{ fmap field f }] are equivalent to
[record.{ field ^= fmap f record.{field} }] if the field exists, and do
nothing otherwise
*)valfmap:<x:'ty;..>get->('ty->'ty)->('afn,t)updateval(|=):<x:'ty;..>get->('ty->'ty)->('afn,t)update(** Field combinator
[ orec.%{ x & y }] is [ orec.%{x}.%{y}]
*)val(&):(any,t)update->(any,t)update->(any,t)updatevaland_then:(any,t)update->(any,t)update->(any,t)update(** Copy a mutable field *)valcopy:<x:'ty;mut:mut;..>get->('afn,t)update(** Delete a field, if the field does not exist, do nothing *)valdelete:<..>get->('adel,t)update(** getter, updater and setter for t *)valget:<ret:'ret;..>get->t->'retvalupdate:(any,t)update->t->tvalset:<x:'ty;mut:mut;..>get->'ty->t->unit(** Operator version of get+update and set *)(** [(.%{} )] operator:
- [ record.%{field} ] returns the value of the field
- [record.%{field ^= value}] returns a functional update of record
- [ record.%{field |= f} ] is equivalent to
[ record.{ field ^= f record.{field} } ]
- [ record.%{delete field}] returns an updated version of record
without this field *)val(.%{}):t->(_*'ret)field_action->'retval(.%{}<-):t-><x:'ty;mut:mut;..>get->'ty->unit(** non-operator version of get,set and update *)valget:<ret:'ret;..>get->t->'retvalupdate:(any,t)update->t->tvalset:<x:'ty;mut:mut;..>get->'ty->t->unit(** Use the type equality implied by the bijection ['a⟺'b] to create
a new ['b] field getter from a ['a] field getter.
The new field getter uses option access *)valtransmute:(<x:'a;mut:'m;..>as'x)get->('a,'b)bijection-><x:'b;mut:'m;ret:'boption>get(** Operator version of [transmute] *)val(@:):(<x:'a;mut:'m;..>as'x)get->('a,'b)bijection-><x:'b;mut:'m;ret:'boption>get(** exception based version of transmute *)valtransmute_exn:(<x:'a;mut:'m;..>as'x)get->('a,'b)bijection-><x:'b;mut:'m;ret:'b>get(** Operator version of [transmute_exn] *)val(@:!):(<x:'a;mut:'m;..>as'x)get->('a,'b)bijection-><x:'b;mut:'m;ret:'b>getend(* Namespace() generates a new module with abstract open record *)moduleMake():S=struct(* Including bijection function to lighten use of the namespace *)include(Bijection)(* Underlying type of the open record *)moduleM=Map.Make(structtypet=U.keyletcompare:U.key->U.key->int=compareend)(** The type of record within the namespace *)typet=eltM.t(** The empty record *)letempty:t=M.emptyletfind_exnwitnessorec=M.find(U.idwitness)orec|>U.extract_exnwitnessletfindwitnessorec=matchfind_exnwitnessorecwith|x->Somex|exceptionNot_found->None(* find the value associated with the key witness,
choose the error handling in function of the access argument *)letfind_gen:typetytya.(ty,tya)access->tywitness->t->tya=funaccesswitnessorec->matchaccesswith|Exn->find_exnwitnessorec|Opt->findwitnessorecletaddkeyval_orec=M.add(U.idkey)(U.B(key,val_))orecletdelete_keykeyorec=M.remove(U.idkey.witness)orec(* Field action : either getter or updater associated to a given key *)type'infofield_action=|Get:<typ:'ty;access:'tya;mut:'m;..>key->(('ty,'m)getter*'tya)field_action|Indirect_get:<typ:'ty;mut:'m;..>key*('ty,'ty2)bijection*('ty2,'tya2)access->(('ty2,'m)getter*'tya2)field_action|Update:<typ:'ty;..>key*'ty->('aconstupdater*t)field_action|Fn_update:<typ:'ty;..>key*('ty->'ty)->('afnupdater*t)field_action|And:('anyupdater*t)field_action*('anyupdater*t)field_action->('anyupdater*t)field_action|Delete:<..>key->('adelupdater*t)field_action(** Alias for the type of fields *)type'infoget=(('a,'mut)getter*'res)field_actionconstraint'info=<x:'a;mut:'mut;ret:'res>type'afield=<x:'a;mut:imm;ret:'aoption>gettype'amut_field=<x:'a;mut:mut;ret:'aoption>gettype'aexn_field=<x:'a;mut:imm;ret:'a>gettype'aexn_mut_field=<x:'a;mut:mut;ret:'a>gettype('param,'t)update=('paramupdater*'t)field_action(** Creation of a new field *)letnew_field_generic=funstorageaccess->Get{witness=U.create();storage;access}letnew_field()=new_field_genericImmOptletnew_field_mut()=new_field_genericMutOptletnew_field_exn()=new_field_genericImmExnletnew_field_exn_mut()=new_field_genericMutExn(** Transform a field getter into a field updater *)letput:typetymret.<x:ty;mut:m;ret:ret>get->ty->('aconst,t)update=funfield_actionx->matchfield_actionwith|Getkey->Update(key,x)|Indirect_get(key,bij,access)->Update(key,bij.fromx)let(^=)fieldx=putfieldx(** Field fmap: [ record.{field |= f } ] is equivalent to
[record.{ field ^= fmap f record.{field} }], if the field exists *)letfmap:typetymret.<x:ty;mut:m;ret:ret>get->(ty->ty)->('afn,t)update=funfield_actionf->matchfield_actionwith|Getkey->Fn_update(key,f)|Indirect_get(key,bij,access)->Fn_update(key,funx->x|>bij.to_|>f|>bij.from)let(|=)fieldf=fmapfieldf(* Perform a copy of a mutable field. Copying an immutable would be pointless *)letcopyfield=field|=(funx->x)(* Delete a field *)letdelete=function|Getkey->Deletekey|Indirect_get(key,bij,access)->Deletekey(* Convert from the stored type 'tys to the core type 'ty *)letderef:typetytysbrand.(ty,tys,brand)storage->tys->ty=funstorageval_->matchstoragewith|Mut->!val_|Imm->val_(* ref_ st · deref st = identity *)letref_:typetytysbrand.(ty,tys,brand)storage->ty->tys=funstorageval_->matchstoragewith|Mut->refval_|Imm->val_letfind_key_exnkeyorec=find_exnkey.witnessorec|>derefkey.storageletfind_key:typetytya.<typ:ty;access:tya;..>key->t->tya=funkeyorec->matchkey.accesswith|Opt->begintrySome(find_key_exnkeyorec)withNot_found->Noneend|Exn->find_key_exnkeyorecletfind_key_with:typety2tya2.(ty2,tya2)access-><typ:'ty;..>key->('ty->ty2)->t->tya2=funaccesskeyforec->matchaccesswith|Exn->find_key_exnkeyorec|>f|Opt->begintrySome(find_key_exnkeyorec|>f)withNot_found->Noneendletadd_keykeyval_orec=addkey.witness(ref_key.storageval_)orecletupdate_keykeyforec=matchfind_key_exnkeyorecwith|x->add_keykey(fx)orec|exceptionNot_found->orec(* get, update and set functions *)letget:<ret:'tya;..>get->t->'tya=funfieldorec->matchfieldwith|Getkey->find_keykeyorec|Indirect_get(key,bijection,access)->find_key_withaccesskeybijection.to_orecletrecupdate:(any,t)update->t->t=funfield_actionorec->matchfield_actionwith|Update(key,x)->add_keykeyxorec|Fn_update(key,f)->update_keykeyforec|Deletekey->delete_keykeyorec|And(l,r)->updater(updatelorec)letand_thenlr=And(l,r)let(&)=and_thenletset:typetyr.<x:ty;mut:mut;ret:r>get->ty->t->unit=funfieldxorec->matchfieldwith|Get{witness;storage=Mut}->(tryfind_exnwitnessorec:=xwithNot_found->())|Indirect_get({witness;storage=Mut},bijection,access)->(tryfind_exnwitnessorec:=bijection.fromxwithNot_found->())(** Operator version of get+update and set *)(** (.{} ) operator:
- [ record.{field} ] returns the value of the field
- [record.{field ^= value}] returns a functional update of record
- [ record.{field |= f} is equivalent to record.{ field ^= f record.{field} }
- [ record.{delete field} returns an updated version of record
without this field *)letrec(.%{}):typekindret.t->(kind*ret)field_action->ret=funorec->function|Getkey->find_keykeyorec|Indirect_get(key,bijection,access)->find_key_withaccesskeybijection.to_orec|Update(key,x)->add_keykeyxorec|Fn_update(key,f)->update_keykeyforec|And(l,r)->orec.%{l}.%{r}|Deletekey->delete_keykeyorec(** The expressions record.{ field ^= value, field2 ^= value2, ... } are
shortcuts for record.{ field ^= value }.{ field2 ^= value2 }... *)let(.%{}<-):typety.t-><x:ty;mut:mut;..>get->ty->unit=funorecfieldx->setfieldxorec(** Create a new open record from a list of field updater :
[create [ field1 ^= value1; field2 ^= value2; ... ] ] *)letcreatel=List.fold_left(funorecfield_action->orec.%{field_action})emptyl(** Use the type equality implied by the bijection 'a<->'b to create a
new ['b] field getter from a ['a] field getter. The new field getter uses
the provided access type *)lettransmute_gen:typetybrand.('ty2,'ty2a)access-><x:ty;mut:'mut;..>get->(ty,'ty2)bijection-><x:'ty2;mut:'mut;ret:'ty2a>get=funaccessaction_fieldbijection->matchaction_fieldwith|Getwitness->Indirect_get(witness,bijection,access)|Indirect_get(witness,bijection',_)->Indirect_get(witness,bijection%bijection',access)lettransmutefieldbijection=transmute_genOptfieldbijectionlet(@:)fieldbijection=transmutefieldbijectionlettransmute_exnfieldbijection=transmute_genExnfieldbijectionlet(@:!)fieldbijection=transmute_exnfieldbijectionend