diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/abstract/Schematics.ml | 8 | ||||
| -rw-r--r-- | lib/concepts/Heading.ml | 100 | ||||
| -rw-r--r-- | lib/concepts/Heading.mli | 24 | ||||
| -rw-r--r-- | lib/concepts/Tuple.ml | 89 | ||||
| -rw-r--r-- | lib/concepts/Tuple.mli | 19 | ||||
| -rw-r--r-- | lib/kernel/Tuple.ml | 137 | ||||
| -rw-r--r-- | lib/protocols/Evaluator.ml | 12 | ||||
| -rw-r--r-- | lib/protocols/Evaluator.mli | 11 | ||||
| -rw-r--r-- | lib/protocols/Schematics.ml | 42 | ||||
| -rw-r--r-- | lib/protocols/Schematics.mli | 31 |
10 files changed, 172 insertions, 301 deletions
diff --git a/lib/abstract/Schematics.ml b/lib/abstract/Schematics.ml deleted file mode 100644 index 9a98a13..0000000 --- a/lib/abstract/Schematics.ml +++ /dev/null @@ -1,8 +0,0 @@ -module type SCHEMA = sig - type t - type attribute - val attributes : t -> (string * attribute) BatFingerTree.t - val project : t -> string BatFingerTree.t -> (t, Concepts.Condition.condition) result - val rename : t -> (string * string) BatFingerTree.t -> (t, Concepts.Condition.condition) result - val merge : t -> t -> (t, Concepts.Condition.condition) result -end diff --git a/lib/concepts/Heading.ml b/lib/concepts/Heading.ml deleted file mode 100644 index 58bbef9..0000000 --- a/lib/concepts/Heading.ml +++ /dev/null @@ -1,100 +0,0 @@ -type t = (string, string) BatMap.t - -let of_alist pairs = List.fold_left (fun acc (name, domain) -> BatMap.add name domain acc) BatMap.empty pairs - -(* Relies on BatMap's iteration order being key order (it is, for the - polymorphic tree map keyed by Stdlib.compare on strings), rather - than sorting again on every call. *) -let attributes heading = - BatMap.foldi (fun name domain acc -> BatFingerTree.snoc acc (name, domain)) heading BatFingerTree.empty - -let names heading = BatMap.foldi (fun name _ acc -> BatSet.add name acc) heading BatSet.empty -let arity heading = BatMap.cardinal heading -let mem name heading = BatMap.mem name heading - -module Error = struct - open Condition - - let unknown_attribute name = - condition "unknown-attribute" "No such attribute on this heading" - ("attribute" |=| Value.String name) - - let incompatible_domain name = - condition "incompatible-domain" "The same attribute name is declared on two different domains" - ("attribute" |=| Value.String name) -end - -let domain_of name heading = - match BatMap.find_opt name heading with - | Some domain -> Ok domain - | None -> Error (Error.unknown_attribute name) - -let project names heading = - let open Utilities.Result in - BatFingerTree.fold_left - (fun acc name -> - let* acc = acc in - let* domain = domain_of name heading in - if BatMap.mem name acc then - Error (Condition.condition "duplicate-attribute" "An attribute was selected more than once" - Condition.("attribute" |=| Value.String name)) - else Ok (BatMap.add name domain acc)) - (Ok BatMap.empty) names - -let rename pairs heading = - let open Utilities.Result in - BatFingerTree.fold_left - (fun acc (old_name, new_name) -> - let* acc = acc in - let* domain = domain_of old_name heading in - if BatMap.mem new_name acc then - Error (Condition.condition "duplicate-attribute" "Two attributes have the same renamed name" - Condition.("attribute" |=| Value.String new_name)) - else Ok (BatMap.add new_name domain acc)) - (Ok BatMap.empty) pairs - -let shared left right = BatSet.intersect (names left) (names right) - -(* TODO: this checks that shared attributes agree on domain, then - takes their union favouring [right]'s bindings for the shared names - (they already agree, so it doesn't matter which side wins). It does - NOT guard against two attributes that happen to share a name and a - domain tag by coincidence rather than by being "the same" join - column. Domain tags are (for now at least) opaque unchecked - strings. Refer to the docs/object-taxonomy.org Schematics section - for more. *) -let joined left right = - let open Utilities.Result in - BatSet.fold - (fun name acc -> - let* acc = acc in - let* dl = domain_of name left in - let* dr = domain_of name right in - if dl = dr then Ok acc else Error (Error.incompatible_domain name)) - (shared left right) (Ok ()) - |> Result.map (fun () -> BatMap.foldi BatMap.add right left) - -let compatible left right = BatMap.equal ( = ) left right - -module rec Representation : (Encoding.Record.S with type t = t) = Encoding.Record.Make (Body) - -and Body : Encoding.Record.BODY = struct - type nonrec t = t - - let tag = 'H' - - let malformed () = - Condition.condition "malformed-heading" - "The on-disk representation of a heading did not conform to what was expected" Condition.empty - - let fields heading = - BatMap.foldi (fun name domain acc -> (name, Encoding.Bencode.String domain) :: acc) heading [] - - let of_fields data = - let open Utilities.Result in - let* kvs = Encoding.Bencode.as_dict data in - kvs - |> List.map (fun (name, v) -> Encoding.Bencode.as_string v |> Result.map (fun d -> (name, d))) - |> Utilities.List.sequence - |> Result.map (List.fold_left (fun acc (name, d) -> BatMap.add name d acc) BatMap.empty) -end diff --git a/lib/concepts/Heading.mli b/lib/concepts/Heading.mli deleted file mode 100644 index 98b8e60..0000000 --- a/lib/concepts/Heading.mli +++ /dev/null @@ -1,24 +0,0 @@ -type t - -val of_alist : (string * string) list -> t - -(** attributes in name order. *) -val attributes : t -> (string * string) BatFingerTree.t - -val names : t -> string BatSet.t -val arity : t -> int -val mem : string -> t -> bool - -module Error : sig - val unknown_attribute : string -> Condition.condition - val incompatible_domain : string -> Condition.condition -end - -val domain_of : string -> t -> (string, Condition.condition) result -val project : string BatFingerTree.t -> t -> (t, Condition.condition) result -val rename : (string * string) BatFingerTree.t -> t -> (t, Condition.condition) result -val shared : t -> t -> string BatSet.t -val joined : t -> t -> (t, Condition.condition) result -val compatible : t -> t -> bool - -module Representation : Encoding.Record.S with type t = t diff --git a/lib/concepts/Tuple.ml b/lib/concepts/Tuple.ml deleted file mode 100644 index dc6eb5f..0000000 --- a/lib/concepts/Tuple.ml +++ /dev/null @@ -1,89 +0,0 @@ -type t = (string, Value.value) BatMap.t - -let empty = BatMap.empty -let is_empty = BatMap.is_empty -let set = BatMap.add -let find_opt = BatMap.find_opt - -module Error = struct - open Condition - - let unknown_attribute name = - condition "unknown-attribute" "No such attribute on this tuple" - ("attribute" |=| Value.String name) -end - -let get name tuple = - match BatMap.find_opt name tuple with - | Some v -> Ok v - | None -> Error (Error.unknown_attribute name) - -let attributes tuple = BatMap.foldi (fun name _ acc -> BatSet.add name acc) tuple BatSet.empty - -let project names tuple = - let open Utilities.Result in - BatFingerTree.fold_left - (fun acc name -> - let* acc = acc in - let* value = get name tuple in - Ok (set name value acc)) - (Ok empty) names - -let rename pairs tuple = - let open Utilities.Result in - BatFingerTree.fold_left - (fun acc (old_name, new_name) -> - let* acc = acc in - let* value = get old_name tuple in - Ok (set new_name value acc)) - (Ok empty) pairs - -let merge left right = - BatMap.foldi - (fun name value acc -> - match acc with - | None -> None - | Some acc -> ( - match BatMap.find_opt name acc with - | None -> Some (BatMap.add name value acc) - | Some value' -> if Value.equal value value' then Some acc else None)) - right (Some left) - -(* Tuple's own wire format for a single value: kept local rather than - added to Concepts.Encoding.Value (which only ever encodes hashes and - options of them today) so this module owns its representation - end to end. *) -let bencode_of_value = function - | Value.String s -> Encoding.Bencode.Tagged ('s', Encoding.Bencode.String s) - | Value.Integer n -> Encoding.Bencode.Tagged ('i', Encoding.Bencode.Int n) - -let malformed_value () = - Condition.condition "malformed-tuple-value" - "A tuple field did not conform to what was expected" Condition.empty - -let value_of_bencode = function - | Encoding.Bencode.Tagged ('s', Encoding.Bencode.String s) -> Ok (Value.String s) - | Encoding.Bencode.Tagged ('i', Encoding.Bencode.Int n) -> Ok (Value.Integer n) - | _ -> Error (malformed_value ()) - -module rec Representation : (Encoding.Record.S with type t = t) = Encoding.Record.Make (Body) - -and Body : Encoding.Record.BODY = struct - type nonrec t = t - - let tag = 'T' - let malformed = malformed_value - - let fields tuple = - BatMap.foldi (fun name value acc -> (name, bencode_of_value value) :: acc) tuple [] - - let of_fields data = - let open Utilities.Result in - let* kvs = Encoding.Bencode.as_dict data in - kvs - |> List.map (fun (name, v) -> value_of_bencode v |> Result.map (fun v -> (name, v))) - |> Utilities.List.sequence - |> Result.map (List.fold_left (fun acc (name, v) -> set name v acc) empty) -end - -let hash tuple = Representation.to_blob tuple |> Hash.hash_of_blob diff --git a/lib/concepts/Tuple.mli b/lib/concepts/Tuple.mli deleted file mode 100644 index 06542c8..0000000 --- a/lib/concepts/Tuple.mli +++ /dev/null @@ -1,19 +0,0 @@ -type t - -val empty : t -val is_empty : t -> bool -val set : string -> Value.value -> t -> t -val find_opt : string -> t -> Value.value option - -module Error : sig - val unknown_attribute : string -> Condition.condition -end - -val get : string -> t -> (Value.value, Condition.condition) result -val attributes : t -> string BatSet.t -val project : string BatFingerTree.t -> t -> (t, Condition.condition) result -val rename : (string * string) BatFingerTree.t -> t -> (t, Condition.condition) result -val merge : t -> t -> t option -val hash : t -> Hash.hash - -module Representation : Encoding.Record.S with type t = t diff --git a/lib/kernel/Tuple.ml b/lib/kernel/Tuple.ml new file mode 100644 index 0000000..e8f2f7f --- /dev/null +++ b/lib/kernel/Tuple.ml @@ -0,0 +1,137 @@ +module Make (S : Abstract.Storage.STORAGE) = struct + module SI = Storage.Make (S) + + module Error = struct + open Concepts.Condition + + let unknown_attribute name = + condition "unknown-attribute" "No such attribute on this tuple" + ("attribute" |=| Concepts.Value.String name) + let malformed_value () = + condition "malformed-tuple-value" + "A tuple field did not conform to what was expected" empty + let incomplete_tuple addr = + condition "incomplete-tuple" + "A stored tuple is missing part of its expected structure. Is your storage corrupted?" + ("address" |=| Concepts.Value.String (Concepts.Hash.to_hum_string addr)) + end + + module Attribute = struct + type t = Concepts.Value.value + + let encode value = + let open Concepts.Encoding in + Bencode.to_blob + (match value with + | Concepts.Value.String s -> Bencode.Tagged ('s', Bencode.String s) + | Concepts.Value.Integer n -> Bencode.Tagged ('i', Bencode.Int n)) + + let decode blob = + let open Utilities.Result in + let open Concepts.Encoding in + let* data = Bencode.of_blob blob in + match data with + | Bencode.Tagged ('s', Bencode.String s) -> Ok (Concepts.Value.String s) + | Bencode.Tagged ('i', Bencode.Int n) -> Ok (Concepts.Value.Integer n) + | _ -> Error (Error.malformed_value ()) + + let domain = function + | Concepts.Value.String _ -> "string" + | Concepts.Value.Integer _ -> "integer" + end + + module AttributeM = Merkle.Interface (S) (Merkle.StringKey) (Attribute) + + type t = { type_ : string; attributes : AttributeM.address } + + module rec Representation : (Concepts.Encoding.Record.S with type t = t) = + Concepts.Encoding.Record.Make (Body) + + and Body : Concepts.Encoding.Record.BODY = struct + type nonrec t = t + + let tag = 'T' + let malformed = Error.malformed_value + + let fields { type_; attributes } = + let open Concepts.Encoding in + [ "type", Bencode.String type_; + "attributes", Value.bencode_of_hash attributes ] + + let of_fields data = + let open Utilities.Result in + let open Concepts.Encoding in + let* type_ = Bencode.field "type" data |> fmap Bencode.as_string in + let* attributes = Bencode.field "attributes" data |> fmap Value.hash_of_bencode in + Ok { type_; attributes } + end + + let encode = Representation.to_blob + let decode = Representation.of_blob + let hash tuple = Representation.to_blob tuple |> Concepts.Hash.hash_of_blob + + let type_of { type_; _ } = type_ + + let node tx { attributes; _ } = + let open Utilities.Result in + AttributeM.find tx attributes + |> fmap (Option.to_result ~none:(Error.incomplete_tuple attributes)) + + let empty tx ~type_ () = + let open Utilities.Result in + let* attributes = AttributeM.empty_under tx in + Ok { type_; attributes } + + let make tx ~type_ attributes = + let open Utilities.Result in + let* node = + AttributeM.with_batch tx (fun () -> + BatMap.String.foldi + (fun name value node -> + let* node = node in + AttributeM.insert tx name value node) + attributes (Ok AttributeM.empty)) + in + Ok { type_; attributes = AttributeM.hash_of node } + + let attribute tx tuple name = + let open Utilities.Result in + let* node = node tx tuple in + let* value = AttributeM.lookup tx name node in + Option.to_result ~none:(Error.unknown_attribute name) value + + let attributes tx tuple = + let open Utilities.Result in + let* node = node tx tuple in + AttributeM.fold_left tx + (fun acc name value -> BatMap.String.add name value acc) + BatMap.String.empty node + + class tuple ~relation conn value node = object (self) + inherit Lifecycle.null + + val tuple = value + val storage = conn + val node = node + + method describe () = + let open Utilities.Result in + let* attributes = + SI.with_transaction storage (fun tx -> + AttributeM.fold_left tx + (fun acc name value -> BatMap.String.add name (Attribute.domain value) acc) + BatMap.String.empty node) + in + Ok (Protocols.Schematics.Tuple { Protocols.Schematics.relation; attributes }) + + method protocols : Protocols.Handle.protocol list = + [Protocols.Schematics.make self] + + method hash = hash tuple + end + + let wrap ~relation conn value = + let open Utilities.Result in + let* node = SI.with_transaction conn (fun tx -> node tx value) in + new tuple ~relation conn value node |> Protocols.Handle.make |> Result.ok +end diff --git a/lib/protocols/Evaluator.ml b/lib/protocols/Evaluator.ml deleted file mode 100644 index 95c2ef8..0000000 --- a/lib/protocols/Evaluator.ml +++ /dev/null @@ -1,12 +0,0 @@ -class type implementation = object - method capabilities : string BatSet.t - method admit : string -> (Handle.t, Concepts.Condition.condition) result -end - -type Handle.protocol += Evaluator of implementation -type t = implementation - -let make impl = Evaluator (impl :> implementation) -let from handle = Handle.into handle (function Evaluator impl -> Some impl | _ -> None) -let capabilities i = Handle.invoke i (fun o -> o#capabilities) -let admit i source = Handle.invoke i (fun o -> o#admit source) diff --git a/lib/protocols/Evaluator.mli b/lib/protocols/Evaluator.mli deleted file mode 100644 index 8400fd2..0000000 --- a/lib/protocols/Evaluator.mli +++ /dev/null @@ -1,11 +0,0 @@ -type t - -class type implementation = object - method capabilities : string BatSet.t - method admit : string -> (Handle.t, Concepts.Condition.condition) result -end - -val make : #implementation -> Handle.protocol -val from : Handle.t -> t Handle.interface option -val capabilities : t Handle.interface -> string BatSet.t -val admit : t Handle.interface -> string -> (Handle.t, Concepts.Condition.condition) result diff --git a/lib/protocols/Schematics.ml b/lib/protocols/Schematics.ml index b0297e7..d2e50ad 100644 --- a/lib/protocols/Schematics.ml +++ b/lib/protocols/Schematics.ml @@ -1,30 +1,24 @@ -module type S = sig - module Schema : Abstract.Schematics.SCHEMA - type t +type multigroup_description = string BatMap.String.t +type relation_description = string BatMap.String.t +type tuple_description = {relation: relation_description; attributes: string BatMap.String.t} - class type implementation = object - method schema : (Schema.t, Concepts.Condition.condition) result - end +(* TODO: For now the description is completely detached from actual + types and domains. Later figure out a way to represent the links + without strings. *) +type description = + | Multigroup of multigroup_description + | Relation of relation_description + | Tuple of tuple_description - val make : #implementation -> Handle.protocol - val from : Handle.t -> t Handle.interface option - val schema : t Handle.interface -> (Schema.t, Concepts.Condition.condition) result - val attributes : - t Handle.interface -> - ((string * Schema.attribute) BatFingerTree.t, Concepts.Condition.condition) result +class type implementation = object + method describe : unit -> (description, Concepts.Condition.condition) result end -module Make (Schema : Abstract.Schematics.SCHEMA) = struct - module Schema = Schema - class type implementation = object - method schema : (Schema.t, Concepts.Condition.condition) result - end +type Handle.protocol += Schematics of implementation - type Handle.protocol += Schematics of implementation - type t = implementation +type t = implementation - let make impl = Schematics (impl :> implementation) - let from handle = Handle.into handle (function Schematics impl -> Some impl | _ -> None) - let schema i = Handle.invoke i (fun o -> o#schema) - let attributes i = Result.map Schema.attributes (schema i) -end +let make impl = Schematics (impl :> implementation) +let from handle = + Handle.into handle (function Schematics impl -> Some impl | _ -> None) +let describe impl = Handle.invoke impl (fun o -> o#describe ()) diff --git a/lib/protocols/Schematics.mli b/lib/protocols/Schematics.mli index d8533bf..dc8f320 100644 --- a/lib/protocols/Schematics.mli +++ b/lib/protocols/Schematics.mli @@ -1,18 +1,21 @@ -(** Instantiate once and share the protocol with schema providers and evaluators. *) -module type S = sig - module Schema : Abstract.Schematics.SCHEMA - type t +type t - class type implementation = object - method schema : (Schema.t, Concepts.Condition.condition) result - end +(* TODO: For now the description is completely detached from actual + types and domains. Later figure out a way to represent the links + without strings. *) +type multigroup_description = string BatMap.String.t +type relation_description = string BatMap.String.t +type tuple_description = {relation: relation_description; attributes: string BatMap.String.t} - val make : #implementation -> Handle.protocol - val from : Handle.t -> t Handle.interface option - val schema : t Handle.interface -> (Schema.t, Concepts.Condition.condition) result - val attributes : - t Handle.interface -> - ((string * Schema.attribute) BatFingerTree.t, Concepts.Condition.condition) result +type description = + | Multigroup of multigroup_description + | Relation of relation_description + | Tuple of tuple_description + +class type implementation = object + method describe : unit -> (description, Concepts.Condition.condition) result end -module Make (Schema : Abstract.Schematics.SCHEMA) : S with module Schema = Schema +val make : #implementation -> Handle.protocol +val from : Handle.t -> t Handle.interface option +val describe : t Handle.interface -> (description, Concepts.Condition.condition) result |
