1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
|
module Error = struct
open Concepts.Condition
let unbound_variable name =
condition "unbound-variable" "A comparison mentioned a variable that nothing has bound"
("variable" |=| Concepts.Value.String name)
let absent_attribute target attribute =
condition "absent-attribute"
"A membership named an attribute that the tuples of its target do not carry"
( "target" |=| Concepts.Value.String (Kernel.Path.to_string target)
& "attribute" |=| Concepts.Value.String attribute )
let open_constraint name variables =
condition "open-constraint"
"A constraint is asserted of every state and so must be closed, and this one leaves \
variables that nothing covers"
( "constraint" |=| Concepts.Value.String name
& "variables" |=| Concepts.Value.String (String.concat ", " variables) )
let violated name =
condition "constraint-violated"
"A candidate state was refused because the violations of a constraint imposed on it are not \
empty"
("constraint" |=| Concepts.Value.String name)
end
type binding_expr = Var of string | Const of Concepts.Value.value
type membership = {target: Kernel.Path.t; binding: binding_expr BatMap.String.t}
type formula =
| Exists of comprehension
| Not of formula
| And of formula * formula
| Or of formula * formula
| Eq of {left: binding_expr; right: binding_expr}
| Truth of bool
and comprehension = {quantifiers: membership list; body: formula}
type scope = Protocols.Handle.t
let variables_of quantifiers =
List.fold_left
(fun acc {binding; _} ->
BatMap.String.values binding
|> BatEnum.fold
(fun acc -> function Var name -> BatSet.String.add name acc | Const _ -> acc)
acc )
BatSet.String.empty quantifiers
let rec free = function
| Truth _ -> BatSet.String.empty
| Not formula -> free formula
| And (left, right) | Or (left, right) -> BatSet.String.union (free left) (free right)
| Eq {left; right} ->
let mentioned = function
| Var name -> BatSet.String.singleton name
| Const _ -> BatSet.String.empty
in
BatSet.String.union (mentioned left) (mentioned right)
| Exists {quantifiers; body} -> BatSet.String.diff (free body) (variables_of quantifiers)
let attributes_of ({quantifiers; _} as comprehension) =
BatSet.String.union (variables_of quantifiers) (free (Exists comprehension))
(* Both takes nested negation; SQL also gets it from outer joins, which this form lacks. *)
type polarity = Positive | Negative | Both
let merged existing polarity =
match existing, polarity with
| Positive, Positive -> Positive
| Negative, Negative -> Negative
| _ -> Both
let at target (candidate, _) = Kernel.Path.equal candidate target
let relevance formula =
let flipped = function Positive -> Negative | Negative -> Positive | Both -> Both in
let record polarity occurrences {target; _} =
let polarity =
match List.find_opt (at target) occurrences with
| None -> polarity
| Some (_, existing) -> merged existing polarity
in
(target, polarity) :: List.filter (Fun.negate (at target)) occurrences
in
let rec walk polarity occurrences = function
| Truth _ | Eq _ -> occurrences
| Not formula -> walk (flipped polarity) occurrences formula
| And (left, right) | Or (left, right) -> walk polarity (walk polarity occurrences left) right
| Exists {quantifiers; body} ->
walk polarity (List.fold_left (record polarity) occurrences quantifiers) body
in
walk Positive [] formula
type environment = Concepts.Value.value BatMap.String.t
(* Cursors a walk holds open are suspended inside it when a caller
abandons the walk, so they are tracked to be closed on release. *)
module Live = struct
let none () = ref []
let opened live handle = live := handle :: !live
let closed live handle =
if List.memq handle !live then begin
live := List.filter (( != ) handle) !live;
Protocols.Handle.release handle
end
let abandoned live =
List.iter Protocols.Handle.release !live;
live := []
end
let resolve scope target = Kernel.Path.lookup scope target
let value_of environment = function
| Const value -> Ok value
| Var name ->
BatMap.String.find_opt name environment
|> Option.to_result ~none:(Error.unbound_variable name)
let fixed environment = function
| Const value -> Some value
| Var name -> BatMap.String.find_opt name environment
let pushed environment binding =
BatMap.String.enum binding
|> BatEnum.fold
(fun pushed (attribute, expression) ->
match fixed environment expression with
| Some value -> BatMap.String.add attribute value pushed
| None -> pushed )
Protocols.Generative.nothing
let agreeing environment expression value =
match expression, fixed environment expression with
| _, Some held -> if Concepts.Value.equal held value then Some environment else None
| Var name, None -> Some (BatMap.String.add name value environment)
| Const _, None -> None
let extend target binding environment tuple =
let open Utilities.Result in
BatMap.String.enum binding
|> BatEnum.fold
(fun acc (attribute, expression) ->
let* acc = acc in
match acc with
| None -> Ok None
| Some environment -> (
match BatMap.String.find_opt attribute tuple.Concepts.Tuple.attributes with
| None -> Error (Error.absent_attribute target attribute)
| Some value -> Ok (agreeing environment expression value) ) )
(Ok (Some environment))
type step = Continue | Stop
let rec holds ~live scope environment formula =
let open Utilities.Result in
match formula with
| Truth truth -> Ok truth
| Not formula -> holds ~live scope environment formula |> Result.map not
| And (left, right) ->
let* left = holds ~live scope environment left in
if left then holds ~live scope environment right else Ok false
| Or (left, right) ->
let* left = holds ~live scope environment left in
if left then Ok true else holds ~live scope environment right
| Eq {left; right} ->
let* left = value_of environment left in
let* right = value_of environment right in
Ok (Concepts.Value.equal left right)
| Exists comprehension ->
let* step = assignments ~live scope environment comprehension ~visit:(fun _ -> Stop) in
Ok (step = Stop)
(* An assignment reached through several tuples is still one
assignment, which the walk settles rather than its callers, since a
relation built over it has to be a set. *)
and assignments ~live scope environment {quantifiers; body} ~visit =
let open Utilities.Result in
let fingerprint environment =
Concepts.Tuple.hash {Concepts.Tuple.type_= ""; attributes= environment}
|> Concepts.Hash.to_raw_string
in
let rec walk seen environment = function
| [] -> (
let* satisfied = holds ~live scope environment body in
let taken = fingerprint environment in
match satisfied, BatSet.String.mem taken seen with
| false, _ | true, true -> Ok (seen, Continue)
| true, false -> Ok (BatSet.String.add taken seen, visit environment) )
| {target; binding} :: rest ->
let* handle = resolve scope target in
let* generative = Protocols.Generative.require handle in
let* cursor = Protocols.Generative.generate generative (pushed environment binding) in
Live.opened live cursor;
Fun.protect
~finally:(fun () -> Live.closed live cursor)
(fun () ->
let* scan = Protocols.Cursor.require cursor in
let rec drain seen =
let* tuple = Protocols.Cursor.next scan in
match tuple with
| None -> Ok (seen, Continue)
| Some tuple -> (
let* extended = extend target binding environment tuple in
match extended with
| None -> drain seen
| Some environment -> (
let* seen, step = walk seen environment rest in
match step with Stop -> Ok (seen, Stop) | Continue -> drain seen ) )
in
drain seen )
in
walk BatSet.String.empty environment quantifiers |> Result.map snd
type expression = {label: string; comprehension: comprehension; state: scope}
let rendered {label; comprehension; _} =
let expression = function
| Var name -> "?" ^ name
| Const value -> "=" ^ Concepts.Value.to_string value
in
let quantifier {target; binding} =
BatMap.String.enum binding
|> BatEnum.map (fun (attribute, held) -> attribute ^ ":" ^ expression held)
|> BatList.of_enum
|> String.concat ";"
|> Printf.sprintf "%s[%s]" (Kernel.Path.to_string target)
in
let rec formula = function
| Truth truth -> if truth then "T" else "F"
| Not inner -> Printf.sprintf "!(%s)" (formula inner)
| And (left, right) -> Printf.sprintf "&(%s,%s)" (formula left) (formula right)
| Or (left, right) -> Printf.sprintf "|(%s,%s)" (formula left) (formula right)
| Eq {left; right} -> Printf.sprintf "eq(%s,%s)" (expression left) (expression right)
| Exists {quantifiers; body} ->
Printf.sprintf "E(%s,%s)"
(List.map quantifier quantifiers |> String.concat "")
(formula body)
in
label ^ "/" ^ formula (Exists comprehension)
let provenance {quantifiers; _} variable =
List.concat_map
(fun {target; binding} ->
BatMap.String.enum binding
|> BatEnum.filter_map (fun (attribute, expression) ->
match expression with
| Var name when String.equal name variable ->
Some {Protocols.Schematics.source= Kernel.Path.to_list target; attribute}
| Var _ | Const _ -> None )
|> BatList.of_enum )
quantifiers
let unknown_domain = ""
let domain_of state origins =
let open Utilities.Result in
match origins with
| {Protocols.Schematics.source; attribute} :: _ -> (
let* handle = resolve state (Kernel.Path.of_list source) in
match Protocols.Schematics.from handle with
| None -> Ok unknown_domain
| Some schematics -> (
let* description = Protocols.Schematics.describe schematics in
match description with
| Protocols.Schematics.Relation description ->
BatMap.String.find_opt attribute description
|> Option.fold ~none:unknown_domain ~some:(fun attribute ->
attribute.Protocols.Schematics.domain )
|> Result.ok
| Protocols.Schematics.Multigroup _ | Protocols.Schematics.Tuple _ -> Ok unknown_domain )
)
| [] -> Ok unknown_domain
module Derivation = struct
type plan = expression
let identity plan = rendered plan |> Bytes.of_string |> Concepts.Hash.hash_of_bytes
let describe {comprehension; state; _} =
attributes_of comprehension
|> BatSet.String.elements
|> List.map (fun variable ->
let origins = provenance comprehension variable in
domain_of state origins
|> Result.map (fun domain -> variable, {Protocols.Schematics.domain; provenance= origins}) )
|> Utilities.List.sequence
|> Result.map BatMap.String.of_list
let modes {comprehension; _} =
let supplied = free (Exists comprehension) |> BatSet.String.elements in
Ok
(Concepts.Mode.of_list
[ Concepts.Mode.decides_when (attributes_of comprehension |> BatSet.String.elements);
Concepts.Mode.generates_when supplied Concepts.Cardinality.Finite ] )
let generate {label; comprehension; state} binding =
let open Utilities.Result in
let heading = attributes_of comprehension in
let tuple_of environment =
{ Concepts.Tuple.type_= label;
attributes= BatMap.String.filter (fun name _ -> BatSet.String.mem name heading) environment
}
in
let live = Live.none () in
let produce ~yield =
let* _ =
assignments ~live state binding comprehension ~visit:(fun environment ->
yield (tuple_of environment);
Continue )
in
Ok ()
in
Ok (Kernel.Generator.cursor_of ~on_release:(fun () -> Live.abandoned live) produce)
end
module Derived = Kernel.EphemeralRelation.Make (Derivation)
module Program = Protocols.Program.Make (struct
type t = expression
end)
let execute expression = Derived.derive expression
class evaluator =
object (self)
inherit Kernel.Lifecycle.null
inherit Kernel.Identity.of_id
method invoke = execute
method protocols : Protocols.Handle.protocol list = [Program.make self]
end
let make () = new evaluator |> Protocols.Handle.make
type t = {name: string; body: formula}
type change = Assertion | Retraction
let declare ~name body =
let uncovered = free body in
if BatSet.String.is_empty uncovered then Ok {name; body}
else Error (Error.open_constraint name (BatSet.String.enum uncovered |> BatList.of_enum))
let name {name; _} = name
let body {body; _} = body
(* Decker: a denial yields its own quantifiers, any other closed
formula denies itself into degree zero. *)
let denial = function
| Not (Exists comprehension) -> comprehension
| formula -> {quantifiers= []; body= Not formula}
let violations {name; body} state = {label= name; comprehension= denial body; state}
let relevant {body; _} ~relation ~change =
match List.find_opt (at relation) (relevance body) with
| None -> false
| Some (_, Both) -> true
| Some (_, Negative) -> change = Assertion
| Some (_, Positive) -> change = Retraction
let with_handle handle body =
Fun.protect ~finally:(fun () -> Protocols.Handle.release handle) (fun () -> body handle)
let vacant relation =
let open Utilities.Result in
let* enumerable = Protocols.Enumerable.require relation in
let* cursor = Protocols.Enumerable.enumerate enumerable in
with_handle cursor (fun cursor ->
let* scan = Protocols.Cursor.require cursor in
let* witness = Protocols.Cursor.next scan in
Ok (Option.is_none witness) )
let satisfied interpreter constrains state =
let open Utilities.Result in
let* relation = Program.invoke interpreter (violations constrains state) in
with_handle relation vacant
let admits interpreter state constraints =
let open Utilities.Result in
List.fold_left
(fun acc constrains ->
let* () = acc in
let* upheld = satisfied interpreter constrains state in
if upheld then Ok () else Error (Error.violated (name constrains)) )
(Ok ()) constraints
|