aboutsummaryrefslogtreecommitdiffstats
path: root/lib/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'lib/protocols')
-rw-r--r--lib/protocols/Cursor.ml7
-rw-r--r--lib/protocols/Cursor.mli5
-rw-r--r--lib/protocols/Generative.ml81
-rw-r--r--lib/protocols/Generative.mli35
-rw-r--r--lib/protocols/Handle.ml1
-rw-r--r--lib/protocols/Handle.mli1
-rw-r--r--lib/protocols/Schematics.ml4
-rw-r--r--lib/protocols/Schematics.mli12
8 files changed, 144 insertions, 2 deletions
diff --git a/lib/protocols/Cursor.ml b/lib/protocols/Cursor.ml
index 36db97d..75dc77d 100644
--- a/lib/protocols/Cursor.ml
+++ b/lib/protocols/Cursor.ml
@@ -26,6 +26,13 @@ let rec next i =
| Some (_, tuple) -> Ok (Some tuple)
| None -> if batch.exhausted then Ok None else next i
+let rec exists i p =
+ let open Utilities.Result in
+ let* found = next i in
+ match found with
+ | None -> Ok false
+ | Some tuple -> if p tuple then Ok true else exists i p
+
let drain i ?(limit = 256) () =
let open Utilities.Result in
let rec loop tuples =
diff --git a/lib/protocols/Cursor.mli b/lib/protocols/Cursor.mli
index 08c7628..c977fec 100644
--- a/lib/protocols/Cursor.mli
+++ b/lib/protocols/Cursor.mli
@@ -14,6 +14,11 @@ val fetch : t Handle.interface -> int -> (batch, Concepts.Condition.condition) r
val next : t Handle.interface -> (Concepts.Tuple.t option, Concepts.Condition.condition) result
+val exists :
+ t Handle.interface ->
+ (Concepts.Tuple.t -> bool) ->
+ (bool, Concepts.Condition.condition) result
+
val drain :
t Handle.interface ->
?limit:int ->
diff --git a/lib/protocols/Generative.ml b/lib/protocols/Generative.ml
new file mode 100644
index 0000000..bd89e21
--- /dev/null
+++ b/lib/protocols/Generative.ml
@@ -0,0 +1,81 @@
+module Error = struct
+ open Concepts.Condition
+
+ let not_generative () =
+ condition "not-generative"
+ "A relation was used where the expression must produce members under a binding, but it \
+ declares no modes. A relation that cannot be asked this way can still be reached through \
+ another access path."
+ empty
+
+ let attributes bound = Concepts.Value.String (String.concat ", " (BatSet.String.elements bound))
+
+ let ungenerable bound =
+ condition "ungenerable-binding"
+ "The relation declares no mode that generates under the attributes bound."
+ ("bound" |=| attributes bound)
+
+ let undecidable bound =
+ condition "undecidable-membership"
+ "The relation declares no mode that decides membership under the attributes of the tuple."
+ ("bound" |=| attributes bound)
+end
+
+type binding = Concepts.Value.value BatMap.String.t
+
+class type implementation = object
+ method modes : (Concepts.Mode.t, Concepts.Condition.condition) result
+ method generate : binding -> (Handle.t, Concepts.Condition.condition) result
+end
+
+type Handle.protocol += Generative of implementation
+type t = implementation
+
+let make impl = Generative (impl :> implementation)
+let from handle = Handle.into handle (function Generative impl -> Some impl | _ -> None)
+let require handle = from handle |> Option.to_result ~none:(Error.not_generative ())
+let nothing = BatMap.String.empty
+let bound = Concepts.Mode.attributes_of_map
+let modes i = Handle.invoke i (fun o -> o#modes)
+
+let generate i binding =
+ Handle.invoke i (fun o ->
+ let open Utilities.Result in
+ let bound = bound binding in
+ let* declaration = o#modes in
+ if Option.is_some (Concepts.Mode.generation declaration bound) then o#generate binding
+ else Error (Error.ungenerable bound) )
+
+let satisfies binding tuple =
+ BatMap.String.for_all
+ (fun attribute value ->
+ match BatMap.String.find_opt attribute tuple.Concepts.Tuple.attributes with
+ | Some found -> Concepts.Value.equal found value
+ | None -> false )
+ binding
+
+class virtual membership =
+ object (self)
+ val virtual heading : Concepts.Mode.attributes
+ method virtual modes : (Concepts.Mode.t, Concepts.Condition.condition) result
+ method virtual generate : binding -> (Handle.t, Concepts.Condition.condition) result
+
+ method contains (tuple : Concepts.Tuple.t) =
+ let open Utilities.Result in
+ let binding = tuple.Concepts.Tuple.attributes in
+ let bound = bound binding in
+ if not (BatSet.String.equal heading bound) then Ok false
+ else
+ let* declaration = self#modes in
+ let* () =
+ if Concepts.Mode.decision declaration bound then Ok () else Error (Error.undecidable bound)
+ in
+ let* cursor = self#generate binding in
+ let expected = Concepts.Tuple.hash tuple in
+ Fun.protect
+ ~finally:(fun () -> Handle.release cursor)
+ (fun () ->
+ let* scan = Cursor.require cursor in
+ Cursor.exists scan (fun member ->
+ Concepts.Hash.hash_equals (Concepts.Tuple.hash member) expected ) )
+ end
diff --git a/lib/protocols/Generative.mli b/lib/protocols/Generative.mli
new file mode 100644
index 0000000..5ada329
--- /dev/null
+++ b/lib/protocols/Generative.mli
@@ -0,0 +1,35 @@
+type t
+
+(** values fixed for some of a relation's attributes, the attributes
+ absent from it being the ones left free. *)
+type binding = Concepts.Value.value BatMap.String.t
+
+class type implementation = object
+ method modes : (Concepts.Mode.t, Concepts.Condition.condition) result
+ method generate : binding -> (Handle.t, Concepts.Condition.condition) result
+end
+
+val make : #implementation -> Handle.protocol
+val from : Handle.t -> t Handle.interface option
+val require : Handle.t -> (t Handle.interface, Concepts.Condition.condition) result
+val modes : t Handle.interface -> (Concepts.Mode.t, Concepts.Condition.condition) result
+
+(** a handle carrying a cursor over the members agreeing with the
+ binding and under the empty binding this is enumeration, so a
+ relation carrying enumerable answers the same members here. Refused
+ when no declared mode generates under the attributes bound. *)
+val generate : t Handle.interface -> binding -> (Handle.t, Concepts.Condition.condition) result
+
+val nothing : binding
+val bound : binding -> Concepts.Mode.attributes
+val satisfies : binding -> Concepts.Tuple.t -> bool
+
+(** membership for a relation with no lookup of its own, asked of the
+ generator under the binding the tuple fixes. Only a tuple carrying
+ exactly the [heading] can be a member, and it must match one whole. *)
+class virtual membership : object
+ val virtual heading : Concepts.Mode.attributes
+ method virtual modes : (Concepts.Mode.t, Concepts.Condition.condition) result
+ method virtual generate : binding -> (Handle.t, Concepts.Condition.condition) result
+ method contains : Concepts.Tuple.t -> (bool, Concepts.Condition.condition) result
+end
diff --git a/lib/protocols/Handle.ml b/lib/protocols/Handle.ml
index c45afb0..41c88dd 100644
--- a/lib/protocols/Handle.ml
+++ b/lib/protocols/Handle.ml
@@ -51,6 +51,7 @@ let equal h1 h2 =
Concepts.Hash.hash_equals (object_of h1)#hash (object_of h2)#hash
let hash h = (object_of h)#hash
+let protocols h = (object_of h)#protocols
let to_string h = (object_of h)#to_string
diff --git a/lib/protocols/Handle.mli b/lib/protocols/Handle.mli
index daee453..40a78ff 100644
--- a/lib/protocols/Handle.mli
+++ b/lib/protocols/Handle.mli
@@ -24,6 +24,7 @@ val equal : t -> t -> bool
val release : t -> unit
val hash : t -> Concepts.Hash.hash
+val protocols : t -> protocol list
val to_string : t -> string
val require : (t -> 'a interface option) -> t -> ('a interface, Concepts.Condition.condition) result
diff --git a/lib/protocols/Schematics.ml b/lib/protocols/Schematics.ml
index d2e50ad..49d8586 100644
--- a/lib/protocols/Schematics.ml
+++ b/lib/protocols/Schematics.ml
@@ -1,5 +1,7 @@
type multigroup_description = string BatMap.String.t
-type relation_description = string BatMap.String.t
+type origin = {source: string list; attribute: string}
+type attribute_description = {domain: string; provenance: origin list}
+type relation_description = attribute_description BatMap.String.t
type tuple_description = {relation: relation_description; attributes: string BatMap.String.t}
(* TODO: For now the description is completely detached from actual
diff --git a/lib/protocols/Schematics.mli b/lib/protocols/Schematics.mli
index dc8f320..fe81943 100644
--- a/lib/protocols/Schematics.mli
+++ b/lib/protocols/Schematics.mli
@@ -4,7 +4,17 @@ type t
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
+
+(** where an attribute comes from with the path relation and the
+ attribute it has. *)
+type origin = {source: string list; attribute: string}
+
+(** an attribute of a derived relation may be for several occurrences
+ at once, for example in a natural join. So its provenance is a
+ collection. *)
+type attribute_description = {domain: string; provenance: origin list}
+
+type relation_description = attribute_description BatMap.String.t
type tuple_description = {relation: relation_description; attributes: string BatMap.String.t}
type description =