aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/Multigroup.ml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel/Multigroup.ml')
-rw-r--r--lib/kernel/Multigroup.ml231
1 files changed, 185 insertions, 46 deletions
diff --git a/lib/kernel/Multigroup.ml b/lib/kernel/Multigroup.ml
index 1417a65..f01a983 100644
--- a/lib/kernel/Multigroup.ml
+++ b/lib/kernel/Multigroup.ml
@@ -3,6 +3,7 @@ module Make (S : Abstract.Storage.STORAGE) = struct
module R = SubstantialRelation.Make (S)
module E = EphemeralRelation.Make (S)
+ (* A relation as a multigroup holds it. *)
module Entry = struct
module Stored = EphemeralRelation.Persisted.Representation
@@ -21,17 +22,39 @@ module Make (S : Abstract.Storage.STORAGE) = struct
Stored.of_bencode data |> Result.map (fun relation -> Ephemeral relation)
| _ -> R.Representation.of_bencode data |> Result.map (fun relation -> Substantial relation)
- let load storage = function
+ let load ?bind storage = function
| Substantial relation -> R.load storage relation
- | Ephemeral relation -> E.load storage relation
+ | Ephemeral relation -> E.load ?bind storage relation
+
+ (* A program is stored as itself, to run whenever it is read; any
+ other relation is stored as the tuples it holds now. *)
+ let admit name h =
+ let open Utilities.Result in
+ let address entry = Concepts.Hash.hash_of_blob (encode entry) in
+ match Program.image h with
+ | Some {evaluator; source; _} ->
+ let* description = Protocols.Relation.heading h in
+ Ok
+ (fun tx ->
+ let* record = E.store tx ~name ~evaluator ~source description in
+ Ok (address (Ephemeral record)) )
+ | None ->
+ let* description, tuples = Protocols.Relation.read h in
+ Ok
+ (fun tx ->
+ let* relation = R.store tx description tuples in
+ Ok (address (Substantial relation)) )
end
+ (* A schema is only a name: \schema\[s] is the tree of its relations,
+ with no record of its own. *)
+ module T = Merkle.Make (S) (Merkle.StringKey)
module RelationM = Merkle.Interface (S) (Merkle.StringKey) (Entry)
- module RMAddressable = Prototype.Addressable.OfTree (S) (Merkle.StringKey)
+ module TAddressable = Prototype.Addressable.OfTree (S) (Merkle.StringKey)
+ module TAssociative = Prototype.Associative.OfTree (S) (Merkle.StringKey)
module RMDirectory = Prototype.Directory.OfTree (S) (Merkle.StringKey) (Entry)
- module RMAssociative = Prototype.Associative.OfTree (S) (Merkle.StringKey)
- type t = {relations: RelationM.address}
+ type t = {schemas: T.address; denials: T.address}
module Error = struct
open Concepts.Condition
@@ -42,42 +65,144 @@ module Make (S : Abstract.Storage.STORAGE) = struct
database corrupted?"
empty
- let incomplete_multigroup addr =
+ let incomplete addr =
condition "incomplete-multigroup"
"A stored multigroup is missing part of its expected structure. Is your storage corrupted?"
("address" |=| Concepts.Value.String (Concepts.Hash.to_hum_string addr))
- end
- module rec Representation : (Concepts.Encoding.Record.S with type t = t) =
- Concepts.Encoding.Record.Make (Body)
+ let unjudgeable denial =
+ condition "unjudgeable-denial"
+ "A denial does not declare that reading it with nothing bound ends, so the state cannot \
+ be judged"
+ ("denial" |=| Concepts.Value.String denial)
+ end
- and Body : Concepts.Encoding.Record.BODY = struct
+ module Representation = Concepts.Encoding.Record.Make (struct
type nonrec t = t
let tag = '*'
let malformed = Error.malformed_multigroup
- let fields {relations} =
+ let fields {schemas; denials} =
let open Concepts.Encoding in
- ["relations", Value.bencode_of_hash relations]
+ ["schemas", Value.bencode_of_hash schemas; "denials", Value.bencode_of_hash denials]
let of_fields fields =
let open Utilities.Result in
let open Concepts.Encoding in
- let* relations = Bencode.field "relations" fields |> fmap Value.hash_of_bencode in
- Ok {relations}
- end
+ let* schemas = Bencode.field "schemas" fields |> fmap Value.hash_of_bencode in
+ let* denials = Bencode.field "denials" fields |> fmap Value.hash_of_bencode in
+ Ok {schemas; denials}
+ end)
let encode = Representation.to_blob
let decode = Representation.of_blob
- class multigroup conn value node =
+ let node_of tx addr =
+ let open Utilities.Result in
+ T.find tx addr |> fmap (Option.to_result ~none:(Error.incomplete addr))
+
+ (* What [h] holds under [property], each to store once the write has
+ begun, and the tree they make when it has. *)
+ let admissions property admit h =
+ let open Utilities.Result in
+ let* listing = Protocols.Handle.require Protocols.Directory.from h in
+ let* found = Protocols.Directory.find listing property in
+ let* children = Option.fold ~none:(Ok []) ~some:Prototype.Directory.children found in
+ List.map
+ (fun (name, child) ->
+ Prototype.Associative.admission admit name child |> Result.map (fun p -> name, p) )
+ children
+ |> Utilities.List.sequence
+
+ let tree_of tx admissions =
+ let open Utilities.Result in
+ let* _ = T.empty_under tx in
+ T.with_batch tx (fun () ->
+ List.fold_left
+ (fun node (name, store) ->
+ let* node = node in
+ let* addr = store tx in
+ T.insert tx name addr node )
+ (Ok T.empty) admissions )
+
+ let admit_schema _ h =
+ let open Utilities.Result in
+ let* relations = admissions "relation" Entry.admit h in
+ Ok (fun tx -> Result.map T.hash_of (tree_of tx relations))
+
+ let relations ?bind storage node =
+ Prototype.mixture_of node (fun make node ->
+ [ TAddressable.make ~node;
+ RMDirectory.make ~storage ~node:(RelationM.from node)
+ ~constructor:(Entry.load ?bind storage);
+ TAssociative.make ~admit:Entry.admit ~storage ~node ~constructor:(fun node' ->
+ Ok (make node') ) ] )
+
+ let rec schema ?bind storage node =
+ let open Utilities.Result in
+ Prototype.mixture
+ [ TAddressable.make ~node;
+ Prototype.Directory.of_properties ["relation", relations ?bind storage node];
+ Prototype.Associative.(
+ of_properties
+ [ ( "relation",
+ update_only (fun relations' ->
+ let* a = Protocols.Handle.require Protocols.Addressable.from relations' in
+ let* node' =
+ SI.with_read storage (fun tx -> node_of tx (Protocols.Addressable.address a))
+ in
+ Ok (schema ?bind storage node') ) ) ] ) ]
+
+ let schemas ?bind storage node =
+ Prototype.mixture_of node (fun make node ->
+ [ TAddressable.make ~node;
+ Protocols.Directory.make
+ object
+ method list = SI.with_read storage (Fun.flip T.keys node)
+
+ method find name =
+ let open Utilities.Result in
+ SI.with_read storage (fun tx ->
+ let* found = T.lookup tx name node in
+ match found with
+ | None -> Ok None
+ | Some addr ->
+ let* relations = node_of tx addr in
+ Ok (Some (schema ?bind storage relations)) )
+ end;
+ TAssociative.make ~admit:admit_schema ~storage ~node ~constructor:(fun node' ->
+ Ok (make node') ) ] )
+
+ let trees tx {schemas; denials} =
+ let open Utilities.Result in
+ let* schemas = node_of tx schemas in
+ let* denials = node_of tx denials in
+ Ok (schemas, denials)
+
+ let held (name, relation) =
+ let open Utilities.Result in
+ Protocols.Handle.releasing relation (fun () ->
+ let* denial = Protocols.Relation.require relation in
+ let* modes = Protocols.Relation.modes denial in
+ if not (Concepts.Mode.exhaustible modes BatSet.String.empty) then
+ Error (Error.unjudgeable name)
+ else
+ let* enumerable = Protocols.Enumerable.require relation in
+ let* cursor = Protocols.Enumerable.enumerate enumerable in
+ Protocols.Handle.releasing cursor (fun () ->
+ let* scan = Protocols.Cursor.require cursor in
+ let* first = Protocols.Cursor.next scan in
+ Ok (Option.to_list (Option.map (fun witness -> name, witness) first)) ) )
+
+ class multigroup ?bind conn value schema_tree denial_tree =
object (self)
inherit Lifecycle.null
method to_string = "multigroup"
val multigroup = value
val storage = conn
- val node = node (* FIXME: see the comment on Branch.ml *)
+ val schema_tree = schema_tree (* FIXME: see the comment on Branch.ml *)
+ val denial_tree = denial_tree
(* TODO: Add later to the lifecycle management, accounting for
derived instances. Callers must release the previous handle
@@ -85,51 +210,65 @@ module Make (S : Abstract.Storage.STORAGE) = struct
last reference is released. *)
method deriving multigroup' =
let open Utilities.Result in
- let* node' =
+ let* schemas', denials' =
SI.with_transaction storage (fun tx ->
- let* _ = SI.store_blob tx (Representation.to_blob multigroup') in
- RelationM.find tx multigroup'.relations
- |> fmap (Option.to_result ~none:(Error.incomplete_multigroup multigroup'.relations)) )
+ let* _ = SI.store_blob tx (encode multigroup') in
+ trees tx multigroup' )
in
- new multigroup storage multigroup' node' |> Protocols.Handle.make |> Result.ok
+ new multigroup ?bind storage multigroup' schemas' denials'
+ |> Protocols.Handle.make
+ |> Result.ok
+
+ method check =
+ let open Utilities.Result in
+ let* named = Prototype.Directory.children (relations ?bind storage denial_tree) in
+ List.map held named |> Utilities.List.sequence |> Result.map List.concat
method protocols : Protocols.Handle.protocol list =
let open Utilities.Result in
+ let derive f node' =
+ Protocols.Handle.require Protocols.Addressable.from node'
+ |> Result.map Protocols.Addressable.address
+ |> fmap (fun addr -> self#deriving (f addr))
+ in
Protocols.
[ Addressable.make self;
+ Admission.make self;
Prototype.Associative.(
of_properties
- [ ( "relation",
- update_only (fun node' ->
- Handle.require Addressable.from node'
- |> Result.map Addressable.address
- |> fmap (fun addr -> self#deriving {relations= addr}) ) ) ] );
+ [ "schema", update_only (derive (fun addr -> {multigroup with schemas= addr}));
+ "denial", update_only (derive (fun addr -> {multigroup with denials= addr})) ] );
Prototype.Directory.of_properties
- [ ( "relation",
- Prototype.mixture_of node (fun make node ->
- [ RMAddressable.make ~node:(RelationM.into node);
- RMDirectory.make ~storage ~node ~constructor:(Entry.load storage);
- RMAssociative.make ~storage ~node:(RelationM.into node)
- ~constructor:(fun node' -> RelationM.from node' |> make |> Result.ok ) ] )
- ) ] ]
-
- method hash = Representation.to_blob multigroup |> Concepts.Hash.hash_of_blob
+ [ "schema", schemas ?bind storage schema_tree;
+ "denial", relations ?bind storage denial_tree ] ]
+
+ method hash = encode multigroup |> Concepts.Hash.hash_of_blob
method address = self#hash
end
- let wrap conn value =
+ let load ?bind conn value =
let open Utilities.Result in
- let* node =
- SI.with_transaction conn (fun tx -> RelationM.find tx value.relations)
- |> fmap (Option.to_result ~none:(Error.incomplete_multigroup value.relations))
- in
- new multigroup conn value node |> Protocols.Handle.make |> Result.ok
+ let* schemas, denials = SI.with_read conn (fun tx -> trees tx value) in
+ new multigroup ?bind conn value schemas denials |> Protocols.Handle.make |> Result.ok
let make conn =
let open Utilities.Result in
SI.with_transaction conn (fun tx ->
- let* empty = RelationM.empty_under tx in
- let multigroup = {relations= empty} in
- let* _ = SI.store_blob tx (Representation.to_blob multigroup) in
- new multigroup conn multigroup RelationM.empty |> Protocols.Handle.make |> Result.ok )
+ let* empty = T.empty_under tx in
+ let multigroup = {schemas= empty; denials= empty} in
+ let* _ = SI.store_blob tx (encode multigroup) in
+ new multigroup conn multigroup T.empty T.empty |> Protocols.Handle.make |> Result.ok )
+
+ (* Handed over as the tree shows one, a directory holding its schemas
+ under \schema and its denials under \denial, a multigroup not stored
+ yet is stored with them. *)
+ let admit _ h =
+ let open Utilities.Result in
+ let* schemas = admissions "schema" admit_schema h in
+ let* denials = admissions "denial" Entry.admit h in
+ Ok
+ (fun tx ->
+ let* schemas = tree_of tx schemas in
+ let* denials = tree_of tx denials in
+ SI.store_blob tx (encode {schemas= T.hash_of schemas; denials= T.hash_of denials}) )
end