diff options
Diffstat (limited to '')
| -rw-r--r-- | README.org | 61 | ||||
| -rw-r--r-- | docs/denial.org | 194 | ||||
| -rw-r--r-- | docs/evaluators.org | 285 | ||||
| -rw-r--r-- | docs/lifecycle.org | 50 | ||||
| -rw-r--r-- | docs/maintenance.org | 104 | ||||
| -rw-r--r-- | docs/modes.org | 171 | ||||
| -rw-r--r-- | docs/object-system.org | 2 | ||||
| -rw-r--r-- | docs/object-taxonomy.org | 8 | ||||
| -rw-r--r-- | docs/roadmap.org | 137 | ||||
| -rw-r--r-- | images/denial-admission.svg | 1 | ||||
| -rw-r--r-- | images/evaluators-classes.svg | 1 | ||||
| -rw-r--r-- | images/evaluators-reading.svg | 1 |
12 files changed, 898 insertions, 117 deletions
@@ -9,7 +9,7 @@ RNT is an experimental database kernel written in OCaml, modeled on the Windows NT kernel. As in NT, the kernel doesn't understand any query language itself. It manages objects (relations, tuples, -branches, sessions, evaluators) and exposes them through named paths, +branches, sessions, programs, evaluators) and exposes them through named paths, handles, and protocols. Query languages and data models are meant to sit on top as /subsystems/, the way Win32 and POSIX sit on top of the NT native API. @@ -85,7 +85,7 @@ Relations, branches, sessions, evaluators and every object live in one object tree. If you can name it, you can reach it: #+BEGIN_SRC ocaml -Path.lookup root ("session" @/ "client" @/ "branch" @/ this) +Path.lookup root ("system" @/ "session" @/ "client" @/ "branch" @/ this) #+END_SRC See [[file:docs/object-system.org][docs/object-system.org]] and [[file:docs/object-taxonomy.org][docs/object-taxonomy.org]]. @@ -112,29 +112,42 @@ supports it. ** Bring your own language -The kernel ships no query language. An evaluator is just another -object that implements the =Evaluator= protocol, so adding a language -means writing one method. Here is one that counts the tuples of a -relation: +The kernel ships no query language. A program names the evaluator that +runs it and carries a kind, a constructor that evaluator adds to +=Program.kind=. An evaluator is an object with the =Evaluator= protocol, +which parses source into its kind and evaluates the kind against a +directory. Here is a language whose programs count the tuples of the +relation at a path: #+BEGIN_SRC ocaml +type Kernel.Program.kind += Count of Kernel.Path.t + +let heading = BatMap.String.singleton "n" {Protocols.Schematics.domain= "integer"; provenance= []} + +let counter = Protocols.Evaluator.make (object - method invoke relation = + method parse source = + Ok (Count (Kernel.Path.of_list (String.split_on_char '\\' source))) + + method eval kind _inputs ~within = let open Utilities.Result in - let* enumerable = Protocols.Enumerable.require relation in - let* cursor = Protocols.Enumerable.enumerate enumerable in - let* scan = Protocols.Cursor.require cursor in - let* tuples = Protocols.Cursor.drain scan () in - Protocols.Handle.release cursor; - let n = BatFingerTree.size tuples in - Ok (Kernel.Prototype.mixture - [Describable (object method describe = string_of_int n end)]) - end) + match kind with + | Count path -> + let* relation = Kernel.Path.lookup within path in + let* _, tuples = Protocols.Relation.read relation in + let n = Concepts.Value.Integer (List.length tuples) in + Ok + (Kernel.EphemeralRelation.literal heading + [{Concepts.Tuple.type_= "count"; attributes= BatMap.String.singleton "n" n}] ) + | _ -> Error Concepts.Condition.(condition "not-a-count" "Not a count program" empty) + end ) #+END_SRC -Register it in the object tree, and it sits next to the first-order -logic evaluator that ships with RNT. See [[file:docs/evaluators.org][docs/evaluators.org]]. +Register it under =\system\evaluator\count=, next to the first-order +logic evaluator that ships with RNT, and a program built with +=Kernel.Program.make ~root ~evaluator:"count"= runs through it. See +[[file:docs/evaluators.org][docs/evaluators.org]]. ** Bring your own storage @@ -162,10 +175,18 @@ RNT is early and its APIs will change. What works today, with tests: - content-addressed tuple storage on LMDB - prolly-tree indexes (chunking is still naive; see =lib/kernel/Merkle.ml=) -- branches, and sessions that pin a snapshot +- branches whose earlier states stay reachable under =previous=, and + sessions that pin a snapshot +- programs that name their evaluator, stored as ephemeral relations and + run against the branch state they are read through - path lookup across the object tree - substantial and ephemeral relations with pull-based cursors -- a FOL evaluator supporting scan and projection +- modes that say what each relation can produce and decide, so that a + relation without end is never read to its end +- denials, which keep a branch head from moving to a state that breaks + them +- a FOL evaluator supporting scan and projection, which reads programs + written in other languages * Building diff --git a/docs/denial.org b/docs/denial.org new file mode 100644 index 0000000..d79a394 --- /dev/null +++ b/docs/denial.org @@ -0,0 +1,194 @@ +#+TITLE: Denials + +* Purpose +A denial is a relation that must be empty. Each tuple it holds is one +breach of a rule, so a state where every denial is empty is a state +where every rule holds. RNT knows no rule language. It knows only that +denials must be empty, and it refuses to move a branch head to a state +where one is not. Constraints across relations, the assertions SQL +names but rarely implements, become relations like any other. + +The idea is the denial form of integrity constraints, from Sadri and +Kowalski ("A theorem-proving approach to database integrity", 1988), +where a rule is written as a query for its own counterexamples. + +* Where denials live +Each multigroup holds its denials beside its schemas. + +#+begin_example +\system\branch\master\multigroup\library\schema\default\relation\book +\system\branch\master\multigroup\library\denial\no-books +#+end_example + +A multigroup stores a denial the way a schema stores a relation. An +[[file:../lib/kernel/EphemeralRelation.ml][ephemeral relation]] that wraps a [[file:../lib/kernel/Program.ml][program]] becomes the program's +evaluator name and source, and any other relation becomes its tuples. A +program is the usual case, because a rule has to be computed against +the state it judges. When a branch reads the denial back, the +[[file:evaluators.org][evaluator]] registered under that name parses the source, and reading +the denial invokes the program =within= the branch state it was read +through. The same denial therefore reads one state through the head +and another through =previous=. + +* Judging a state +[[file:../lib/protocols/Admission.mli][Admission]] is the protocol that judges a state. + +#+begin_src ocaml +type violations = (string * Concepts.Tuple.t) list + +class type implementation = object + method check : (violations, Concepts.Condition.condition) result +end +#+end_src + +=check= names every denial that holds a tuple, with one such tuple as +its witness. An empty list means the state is admitted. An error means +the state could not be judged, and an unjudged state is not admitted +either. + +A multigroup judges each of its denials in two steps. + +1. It asks the denial for its [[file:modes.org][modes]]. If the denial is not + exhaustible with nothing bound, the multigroup fails with + =unjudgeable-denial= and reads nothing. +2. Otherwise it reads the denial up to its first tuple. No tuple means + the denial holds. A first tuple is the witness, and the multigroup + reads no further. + +For a denial that wraps a program, the multigroup invokes the program +once, within the branch state it judges, and asks both questions of the +relation the program returns. + +A branch state asks each of its multigroups and names every violation +by its path, =\multigroup\[m]\denial\[d]=. Judging belongs to the +multigroup, the universe its relations describe. The branch only +versions states and collects the answers. A rule may still read +relations of any multigroup, because the branch asks all of them. + +One witness is enough to refuse a state, and asking for more would cost +a read of the whole denial, which never ends for a =Countable= one. + +* Trust +The guard rests on what a denial declares. A FOL denial over stored +relations derives its modes from theirs, so its claim to be finite +holds. A denial written in another language declares whatever its +evaluator returns. A program that declares =Finite= and then never ends +still hangs the check, and RNT cannot tell. The same holds when FOL +reads a relation from another evaluator, since FOL takes that +relation's modes as given. A cycle of programs across languages also +recurses without end. + +* Moving a head +=BranchManager= asks the proposed state for its admission before it +moves a head. If the state is not admitted, it refuses the move with a +=refused= condition that names each denial and its witness, and the +head stays where it was. It asks before taking the head, since a denial +may run a program that reads storage. + +#+begin_src plantuml :file ../images/denial-admission.svg +@startuml +participant "Scope.publish" as Publish +participant BranchManager +participant "proposed\nbranch state" as Branch +participant Multigroup +participant Denial + +Publish -> BranchManager : move the head +BranchManager -> Branch : check +loop each multigroup + Branch -> Multigroup : check + loop each denial + Multigroup -> Denial : modes + alt not exhaustible with nothing bound + Multigroup --> BranchManager : unjudgeable-denial + else exhaustible + Multigroup -> Denial : enumerate, read the first tuple + Denial --> Multigroup : a witness, or nothing + end + end + Multigroup --> Branch : violations +end +Branch --> BranchManager : violations, named by path +alt none + BranchManager -> BranchManager : move the head +else some + BranchManager --> Publish : refused, one witness per denial +end +@enduml +#+end_src + +#+RESULTS: +[[file:../images/denial-admission.svg]] + +The check judges the state itself, not a change to it. Every commit +lands only if the whole state it makes is admitted, whichever subsystem +made it, so a rule written in one language guards data written from +all of them. + +* Publishing denials +[[file:../lib/kernel/Scope.mli][Scope.publish]] binds denials with its relations, in the same state. + +#+begin_src ocaml +Scope.publish root ~branch:"master" ~home:library + ~denials:["no-books", Some no_books; "audit:shelved-twice", Some twice] + ["book", Some books] +#+end_src + +A name =d= binds in the home multigroup, and =m:d= binds in multigroup +=m=. A commit can install a rule and the data it guards together, and +the rule is judged with the data. If the state it makes already breaks +the rule, the commit is refused. A denial that cannot be judged cannot +be installed either, since the state that holds it cannot be admitted. + +* Judging a state without landing it +A subsystem that wants to know whether a change would be admitted builds +the state with [[file:../lib/kernel/Path.mli][Path.assoc_all]] and asks it. + +#+begin_src ocaml +let* next = Path.assoc_all head [Scope.relations library, "book", Some books] in +let* admission = Protocols.Admission.require next in +Protocols.Admission.check admission +#+end_src + +Nothing lands. A host can use this to preview a form, a move or an +import, and show which rule would refuse it. + +* Open problems +** Incremental checking +Every commit judges every denial of every multigroup over the whole +state. Decker ("Translating Advanced Integrity Checking Technology to +SQL", 2002) shows how to check only what a change can break. Take the +denial of a book shelved twice. + +#+begin_example +{(title, s1, s2) | shelved(title, s1) ∧ shelved(title, s2) ∧ s1 ≠ s2} +#+end_example + +A commit that inserts =shelved("Codd", "A")= can only add violations +where =title = "Codd"= and one shelf is ="A"=. Asking the denial for +those tuples, with the other shelf free, is the whole incremental check. +It is the insertion-only case of [[file:maintenance.org][incremental maintenance]], which covers +the delta rules it reuses. + +RNT cannot ask that today. The question binds some attributes of the +denial and leaves others free, and no operation produces the tuples of +a relation under a partial binding. This is the gap that [[file:modes.org::*Reachable bindings][reachable +bindings]] describes. Closing it takes three pieces. + +1. A binding-aware read, which every relation implements and FOL passes + through its plans. +2. Indexes, so that a substantial relation answers such a read by + lookup rather than by filtering a scan. +3. Read sets, so that RNT knows which relations a denial reads and how + their attributes line up with the denial's, and can turn an inserted + tuple into bindings on each denial it touches. + +** Denials do not protect themselves +A commit that removes a denial and breaks its rule is judged on the +state it makes, where the denial is gone, so it lands. Guarding +=\denial= needs the access checks the [[file:../README.org][README]] plans to keep apart +from lookup. + +** Multigroups from before denials +A multigroup stored before denials existed has no =denials= field and +does not load. diff --git a/docs/evaluators.org b/docs/evaluators.org index b7ce3b8..f1b4be5 100644 --- a/docs/evaluators.org +++ b/docs/evaluators.org @@ -1,108 +1,215 @@ -#+TITLE: Evaluators +#+TITLE: Programs and evaluators + +* Why the kernel has no language +RNT supplies objects, storage and the execution lifecycle, and leaves +languages to its clients. SQL puts retrieval, data manipulation and data +definition in one language, and the boundaries between them are hard to +draw. In RNT, a language =A= may serve retrieval, =B= may serve +manipulation and definition, and =C= may serve manipulation beside =B=. +Nothing arbitrates between =B= and =C=, and a client picks either. The +[[file:../lib/evaluators/FOL.ml][FOL]] evaluator is one such language, shipped with RNT for retrieval. + +That leaves the kernel two jobs. It has to run a program without +knowing its language, and it has to let programs in different languages +read each other's results. Programs and evaluators are how it does both. + +* Programs +The kernel holds, stores and runs programs whose structure it cannot +know. A [[file:../lib/kernel/Program.ml][program]] is therefore an image, in the manner of a Windows +[[https://en.wikipedia.org/wiki/Portable_Executable][Portable Executable]], whose header tells the loader which subsystem +runs it. -* Position -RNT should supply objects, storage, and execution -lifecycle, but not directly a sublanguage. That duty is then on the -clients to implement one and register it, using the kernel's -capabilities. +#+begin_src ocaml +type kind = .. +type image = {evaluator: string; kind: kind; source: string} +#+end_src -In this context, an evaluator is an RNT object, registered and -selected through the object interface, with a special "evaluator" -protocol. Unlike SQL where the boundaries between execution contexts -of the language is confusing and sometmes non-trivial, a sublanguage -=A= may serve retrieval, =B= may serve DML and DDL while =C= may serve -DML alongside =B=. Nothing arbitrates between B and C; making the -client free to opt for one. +=evaluator= names the evaluator that runs the program, as +=scope:language=. =kind= is the program as that evaluator understands +it. =source= is its text, for display and for storage. -The FOL evaluator is therefore, one of such evaluators that is shipped -alongside RNT for specifically Data Retrieval. +=kind= is an [[https://ocaml.org/manual/extensiblevariants.html][extensible variant]], extended the way [[file:object-system.org][protocols]] are. Each +evaluator adds its own constructor, as FOL adds =Fol of Path.t term=. +The tree keeps its OCaml type, and only the evaluator that defined the +constructor can match on it, so the kernel passes programs around with +no encoding in between. The evaluator name is the one string a program +carries, because the kernel looks evaluators up by name and storage can +keep a name but not a constructor. -* Capability tags -An evaluator object declares a set of opaque labels — =drl=, =dml=, -=ddl=, =vcs=, or anything a client invents. The registry stores and -indexes them so a client can ask which evaluators declare =dml= and -get back B and C. - -* Protocols -An evaluator is an object like any other, that can be used to execute -a program, exactly as an interpreter takes an expression and an -environment. The plan tree should already bear every value and handle -the program needs, so there is nothing left to bind. Each invocation -carries independent state based atop of the handles it was given -(being therefore hash dependent and isolated). +Every program speaks [[file:../lib/protocols/Executable.mli][Executable]], and a program built with a root can run. #+begin_src ocaml -module Make (P : PROGRAM) : sig - class type implementation = object - method invoke : P.t -> (Handle.t, condition) result - end -end +method invoke : Handle.t list -> within:Handle.t -> (Handle.t, condition) result #+end_src -The protocol is parameterized by the evaluator's own plan type, so a -plan is never encoded into values only to be decoded again. Each -application of the functor yields its own protocol constructor, which -is what keeps one evaluator from answering another's programs: a -client obtains the interface through the module of the evaluator it -intends to speak to. The FOL evaluator, which lives under -=lib/evaluators= alongside the evaluators shipped with RNT, currently -understands a single node: +Invoking a program looks up =\[scope]\evaluator\[language]= under its +root, and hands that evaluator the kind, the inputs and =within=. A bare +=language= names the =system= scope. The root is a capability, so a +program reaches only the evaluators of the root it was given. A program +built without a root can be stored but not run, and invoking it fails +with =unloaded-program=. A name with nothing registered under it fails with +=no-evaluator=. + +* Evaluators +An evaluator is an object with the [[file:../lib/protocols/Evaluator.mli][Evaluator]] protocol. #+begin_src ocaml -type plan = Base of Handle.t +method parse : string -> (kind, condition) result +method eval : kind -> Handle.t list -> within:Handle.t -> (Handle.t, condition) result #+end_src -=Base= carries the handle of a relation, which is enumerated to -exhaustion. The evaluator only invokes protocols on the handles it -receives, which keeps it free of storage and of name resolution. - -The returned handle carries a result protocol with =Protocols.Cursor=, -giving it incremental consumption. Since executing a plan node yields -a cursor, operators stack as cursors over cursors, in the manner of a -volcano iterator, and no intermediate result is materialized. A -successor state and a command completion are further result protocols, -added for DML and DDL later. It's important to notice that the kernel -enumerates result protocols; it thererfore does not enumerate -sublanguages. That is not to say however that we cannot have a builtin -catalog relation installed and configured by the client that -enumerates them (that's a concern for the future). - -* Handles -There is no evaluation context. Since we are a capability based -system, holding a handle is what authorizes its use, so the caller -obtains every handle a program needs beforehand (through the -directories it was granted) and passes them in. Evaluators reach -kernel resources *only* through those handles. A program that refers -to things by name carries the directory they are resolved in, as the -lambda evaluator does: - -#+begin_src ocaml -type program = Program of term * Directory.t Handle.interface +=eval= runs a kind, and fails for a kind that is not its own. =parse= +turns source back into a kind, which is how the kernel reloads a stored +program. A program built in code skips parsing. =FOL.program term= +builds one from a tree and prints the tree as its source, so a test can +build a plan without a syntax. FOL has no text syntax yet, and its +source is the [[https://en.wikipedia.org/wiki/Bencode][bencoded]] term. + +#+begin_src plantuml :file ../images/evaluators-classes.svg +@startuml +hide empty members +skinparam classAttributeIconSize 0 + +class Program <<image>> { + evaluator : string + kind : Program.kind + source : string +} +interface Executable { + invoke(inputs, within) : relation +} +interface Evaluator { + parse(source) : kind + eval(kind, inputs, within) : relation +} +class "FOL evaluator" as FOL +class EphemeralRelation +interface Relation { + contains(tuple) : bool + modes() : Mode.t +} +interface Enumerable { + enumerate() : cursor +} +interface Schematics { + describe() : heading +} + +Program ..|> Executable : runs when given a root +FOL ..|> Evaluator +Program --> Evaluator : names it as\nscope:language +EphemeralRelation ..|> Relation +EphemeralRelation ..|> Enumerable +EphemeralRelation ..|> Schematics +EphemeralRelation o--> "0..1" Program : wraps +Evaluator ..> EphemeralRelation : eval returns +@enduml #+end_src -Since every object is content addressed and prolly trees do not -depend on insertion order, equal state yields an equal hash, so the -relation behind a handle does not change under a running evaluation. -A successful evaluation and cursor exhaustion never commit. Commit -should be an explicit host operation. +#+RESULTS: +[[file:../images/evaluators-classes.svg]] + +* Within +In RNT, holding a handle is what authorizes its use, so an evaluator +gets no context beyond one directory. A program names relations by +path, and its evaluator resolves those paths in the directory the +program is invoked =within=. It can reach nothing else. + +Resolving paths at each invocation is what lets one program run against +any state. Read through +=\system\branch\master\multigroup\m\schema\s\relation\r=, a stored +program runs within that branch state. Read through +=\system\branch\master\previous\...=, it runs within the previous state +and reads what was there then. It can never read outside its branch. + +Evaluation never commits, and neither does exhausting a cursor. +Committing is an explicit host operation. Cancellation, budgets and +resource accounting are not implemented. + +* Results +Programs in different languages can read each other's results only if +those results share one interface. The result of every program is an +[[file:../lib/kernel/EphemeralRelation.ml][ephemeral relation]]. It describes its heading, declares its [[file:modes.org][modes]] and +answers membership. Enumerating it gives a cursor, so operators stack +as cursors over cursors, as in Graefe's Volcano iterator model +("Volcano, an Extensible and Parallel Query Evaluation System", 1994), +and nothing in between is materialized. + +An ephemeral relation can wrap a program, through +=EphemeralRelation.of_program=. Reading it invokes the program within +the state it was given. Asking its modes, or whether it holds a tuple, +invokes the program and asks the relation the program returns. For FOL +this is cheap, because FOL builds its result without reading a tuple. + +Programs that change state have no result protocol yet. + +* Storage +A multigroup stores an ephemeral relation that wraps a program as the +program's evaluator name and source, beside its heading. It stores any +other relation as its tuples. When a branch reads the relation back, +the evaluator registered under that name parses the source into a kind, +and the relation wraps the program that comes out. The parse happens at +lookup, so a missing evaluator or a malformed source fails at +=Path.lookup= rather than at the first read. + +#+begin_src plantuml :file ../images/evaluators-reading.svg +@startuml +actor Client +participant "Path.lookup" as Path +participant Multigroup +participant EphemeralRelation as E +participant Program +participant "evaluator under\nthe program's name" as Ev + +Client -> Path : lookup a relation in a branch state +Path -> Multigroup : find it +Multigroup -> E : load (evaluator name, source, heading) +E -> Program : Program.load root +Program -> Ev : parse source +Ev --> Program : kind +E --> Client : relation wrapping the program +Client -> E : enumerate +E -> Program : invoke ~within:branch state +Program -> Ev : eval kind ~within +Ev --> Program : result relation +Program --> E : result relation +E --> Client : cursor over the result +@enduml +#+end_src -Cancellation, budgets, and resource accounting are not implemented. +#+RESULTS: +[[file:../images/evaluators-reading.svg]] * Composition -Evaluators compose through invocation and result protocols alone. A -DML program written in B may invoke a retrieval program written in A, -consume its cursor, and build a successor state from the tuples. A -retrieval program in A may invoke a procedural relation implemented by -C. Neither evaluator however knows the other's representation. The -caller must establish that the callee's contract meets its needs, -obviously. +Evaluators compose through programs and results, and neither knows the +other's kind. When FOL resolves a path to a relation that wraps a +program, it checks the program's kind. + +- A FOL program is inlined into the plan. FOL then sees through it, and + refuses a program that reaches itself with =cyclic-program=. +- A program of any other kind is invoked within the same state, and its + result enters the plan like any other relation. + +A program in another language reads a FOL program the same way, by +resolving its path and reading the relation. The [[file:../test/unit/kernel/Branch.ml][branch tests]] do both, +with a toy language whose programs shout the titles of a FOL relation. + +FOL detects cycles only among its own programs. A cycle across +languages, where a FOL program reads a toy program that reads it back, +recurses without end. Nothing checks one evaluator's assumptions about +another's results either, so the caller has to establish that the +callee's results meet its needs. * Registration -Registration is made by the client, and in the case of builtins, -automatically at initialization, just as =Relation= and =Schematics=, -for example. A foreign registration path is a later concern and is not -designed for here. - -Only =Protocols.Cursor= exists as a result protocol (at least for now), -so a successor state, completion, plan persistence, and cross-evaluator -optimizations are absent by design. +The system's evaluators are registered at initialization, under +=\system\evaluator=. A subsystem mounts its own root with +[[file:../lib/kernel/Initialization.mli][Initialization.mount]], which holds =\[name]\evaluator=, and registers +its evaluators there. Its programs then name them as =name:language=. A +foreign registration path is a later concern. + +* Capability tags +A client should be able to ask which evaluators serve manipulation, and +get =B= and =C= back. The intent is that each evaluator declares a set +of opaque labels, such as =drl=, =dml=, =ddl=, =vcs= or any a client +invents, and that the registry indexes them. None of this is +implemented. diff --git a/docs/lifecycle.org b/docs/lifecycle.org index 80b387d..c23e9a1 100644 --- a/docs/lifecycle.org +++ b/docs/lifecycle.org @@ -7,6 +7,9 @@ operates purely on the object tree and never touches storage. "Collection" here means dropping the in-memory registry entry, not deleting anything on disk. +The manager is a design and is not built. [[*What exists today][What exists today]] describes the +mechanism the code has now. + * Three independent facts per object Each registry entry keeps three separate pieces of state, deliberately not @@ -42,12 +45,11 @@ the =reference_count= of each edge target; unregistration drops it. The edge set is a per-kind property and lives next to the registry, because the object knows its own shape. -Only ephemeral relations carry edges today. An ephemeral relation is a stored -plan that reads other relations, so those inputs must not be collected while -the plan can still be replayed. The edges are branch-local symlink paths, not -version-pinned paths: as the branch head advances the inputs move with it, and -=Namespace.resolve= turns the symlink into whatever object is current on this -branch hash. This is what lets an ephemeral relation behave like a view. +Only ephemeral relations carry edges today. An ephemeral relation wraps a +[[file:evaluators.org][program]] that reads other relations, so those inputs must not be collected +while the program can still run. Its inputs are paths resolved in the branch +state it is read through, not pinned versions. Read through the head, it reads +the inputs as they are. Read through =previous=, it reads them as they were. * Teardown order @@ -80,3 +82,39 @@ collectable like anything else. Separate from collection. An exclusive object with an open handle, such as a branch head being read, cannot be modified until the handle closes. =contention= reports this so writers can be gated. + +* What exists today +Each object carries its own lifecycle, one of the classes in +[[file:../lib/kernel/Lifecycle.mli][Lifecycle]], and every handle to it calls into that lifecycle when it is +copied or released. + +- =null= is for objects with nothing to release, either because storage + can rebuild them or because they hold no resources. Substantial + relations, multigroups, branch states, programs and evaluators are + =null=. +- =counted= is for objects that hold resources. It counts handles and runs + the object's destructor when the last one is released. Namespaces, + sessions, generated cursors and ephemeral relations are counted, and an + ephemeral relation releases its inputs when it is destroyed. +- =exclusive= admits one handle at a time. Nothing uses it since sessions + became counted. +- =retaining= stands in for a handle while it holds a reference to an + owner, so that what the handle reads outlives the object that produced + it. A cursor over a program's result keeps the result alive this way. + +A namespace owns the handles registered in it. =Namespace.update= moves the +handle in, which invalidates the caller's handle, and releases the handle +it replaces. A destroyed namespace releases every entry. A caller that +still needs a handle after registering it registers a =Handle.copy=. + +Walking a path opens a handle at every step, and the walk has to release +all of them except the last. [[file:../lib/kernel/Path.ml][Path]] does this with an autorelease pool, +named after the [[https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html][Cocoa]] mechanism. Each handle found along the way goes +into the pool, and the pool releases them when the walk ends. =Handle.keep= +takes the handle the walk returns back out of the pool, and the caller +releases it. The pool is an OCaml [[https://ocaml.org/manual/effects.html][effect handler]], so the code inside the +walk adds handles to it without being passed the pool. + +A handle to a managed object that the garbage collector reaches while it +is still valid has leaked. Its finalizer logs a warning with the backtrace +of where the handle was made, and releases it. diff --git a/docs/maintenance.org b/docs/maintenance.org new file mode 100644 index 0000000..42b5075 --- /dev/null +++ b/docs/maintenance.org @@ -0,0 +1,104 @@ +#+TITLE: Incremental maintenance + +* Purpose +An [[file:../lib/kernel/EphemeralRelation.ml][ephemeral relation]] is computed when it is read. That is right for a +relation read rarely, and wrong for one read on every commit or over a +large input, where each read repeats the whole computation. A +materialized ephemeral relation would store its tuples, so that reading +it costs what reading a substantial relation costs. Every commit that +changes its inputs then has to change it as well. + +Recomputing it from scratch on each commit costs as much as the +relation itself. Incremental maintenance computes only the change, from +the change to the inputs. The literature calls this [[https://en.wikipedia.org/wiki/Materialized_view][incremental view +maintenance]], and Gupta and Mumick survey it in "Maintenance of +Materialized Views: Problems, Techniques, and Applications" (1995). +RNT has none of it yet. This document is the design it should follow. + +* Deltas +A commit changes each relation it touches by a set of inserted tuples, +Δ⁺, and a set of deleted tuples, Δ⁻. For a relation =D= derived from +inputs =R₁ … Rₙ=, maintenance computes =ΔD⁺= and =ΔD⁻= from the =ΔRᵢ= and +the states before and after the commit, operator by operator. + +- =Base r= changes exactly as =r= does. +- =Project (p, A)= gains the projection of each tuple in =Δp⁺= that it + did not hold before. It loses the projection of a tuple in =Δp⁻= only + if no other tuple of =p= projects to the same tuple, which is the + problem [[*Counting][counting]] solves. +- A join, once [[file:../lib/evaluators/FOL.ml][FOL]] has one, gains =ΔR⁺ ⋈ S= and =R ⋈ ΔS⁺=, against the + right states. Computing =ΔR⁺ ⋈ S= means asking =S= for the tuples that + agree with each inserted tuple of =R= on the join attributes. + +That last question binds some attributes of =S= and leaves the rest +free. It is the partial binding that no operation in RNT can ask +today, the gap [[file:modes.org::*Reachable bindings][reachable bindings]] describes. Without it, the only way +to compute a delta through a join is to read =S= whole, which is +recomputation by another name. + +* Counting +Deletion through a projection needs to know how many tuples of the input +produce each tuple of the output. A materialized relation can store that +count beside each tuple. An inserted input tuple raises the count of +its projection, a deleted one lowers it, and the projected tuple leaves +the relation when its count reaches zero. This is the counting algorithm +of Gupta, Mumick and Subrahmanian ("Maintaining Views Incrementally", +1993). The same paper gives DRed for recursive relations, where counts +do not terminate, by deleting an overestimate and rederiving what +survives. + +* Denials are the simplest case +A [[file:denial.org][denial]] is an ephemeral relation that must stay empty, and checking it +incrementally is maintenance with most of the work removed. + +- Only insertions matter. The state before the commit was admitted, so + every denial was empty, and an empty relation has nothing to lose. + =ΔD⁻= is always empty. +- Only emptiness matters. One tuple of =ΔD⁺= is enough to refuse the + commit, so the check stops at the first. +- Nothing is stored. A denial is never materialized, and its delta is + thrown away once judged. +- Nothing is counted, because nothing is ever deleted from a denial. + +Incremental denial checking therefore reuses the insertion half of the +delta rules, stopped at the first tuple. Full maintenance adds +deletions, counts and storage on top of it. + +* What it needs +Some pieces are shared with incremental denial checking. + +1. A binding-aware read, which every relation implements and FOL passes + through its plans, to compute deltas through joins. +2. Indexes, so that such a read is a lookup on a substantial relation + rather than a filtered scan. +3. Read sets, so that RNT knows which relations a derived relation + reads and how their attributes line up with its own, and can tell + which relations a commit touches. +4. Tuple deltas. [[file:../lib/kernel/Path.mli][Path.diff]] names the relations that changed between + two states, and skips subtrees with equal addresses. The tuples that + changed inside a relation can be found the same way, by diffing its + two tuple sets, since a prolly tree shares every unchanged subtree. + RNT does not expose that diff yet. + +Others belong to maintenance alone. + +5. Materialization, so that a derived relation can be stored with its + tuples and counts, and read like a substantial relation. +6. Deletion rules and counts, as above. + +* When maintenance runs +A commit would apply its changes to the base relations, maintain every +materialized relation that reads them, then judge the denials against +the resulting state, and only then move the head. Maintenance has to +come before judgement, because a denial may read a materialized +relation. The maintained relations land in the same state as the change +that caused them, so no state ever holds a stale one. + +* Checking it +A tuple set is content addressed. If the shape of its tree depended only +on the tuples it holds, and not on the order they were inserted in, a +maintained relation and the same relation recomputed from scratch would +have equal addresses exactly when maintenance was correct, and +comparing the two addresses would test the delta rules completely. The +chunking in [[file:../lib/kernel/Merkle.ml][Merkle]] is still naive and does not promise that, so until +it does, the test has to compare the tuples themselves. diff --git a/docs/modes.org b/docs/modes.org new file mode 100644 index 0000000..2337b29 --- /dev/null +++ b/docs/modes.org @@ -0,0 +1,171 @@ +#+TITLE: Modes + +* Extension and intension +A relation can be known in two ways. Its [[https://en.wikipedia.org/wiki/Extensional_and_intensional_definitions][extension]] is the set of tuples it +holds, and a tuple belongs to it if it is among them. Its intension is +the predicate those tuples satisfy, and a tuple belongs to it if the +predicate says so. A finite relation can be answered from either. A +relation of [[https://en.wikipedia.org/wiki/Aleph_number][countably infinite]] cardinality can only be answered from +its intension, since enumerating it never ends. + +A reader that waits for the end of an endless relation hangs, so RNT +has to know before reading whether a read ends, and whether a relation +can decide membership at all. Modes are how a relation says so. A mode +states what a relation can answer under a given binding of its +attributes, and says nothing of whether the answer comes from the +extension or the intension. The idea comes from logic languages such +as Mercury, where a predicate declares its [[https://mercurylang.org/information/doc-latest/mercury_ref/Modes.html][modes]], which arguments must +be bound for a call, and its [[https://mercurylang.org/information/doc-latest/mercury_ref/Determinism.html][determinism]], how many solutions such a +call has. + +* A mode +[[file:../lib/concepts/Mode.mli][Mode]] describes one with two fields. + +#+begin_src ocaml +type mode = {bound: attributes; yields: Cardinality.t} +#+end_src + +Given values for the attributes in =bound=, the relation produces every +tuple that agrees with them, and there are at most =yields= of them. +[[file:../lib/concepts/Cardinality.mli][Cardinality]] has four classes, =Empty=, =Bounded n=, =Finite= and +=Countable=. Every class but =Countable= is exhaustible, meaning that +enumeration ends. + +A relation declares its modes through the [[file:../lib/protocols/Relation.mli][Relation]] protocol. + +#+begin_src ocaml +method modes : (Concepts.Mode.t, Concepts.Condition.condition) result +#+end_src + +* Generation under a binding +A mode declared for =B= also serves any binding that includes =B=. The +relation produces the tuples that agree with =B= and drops those that +disagree with the extra values, so it produces no more than before. + +=Mode.generation modes bound= is therefore the tightest =yields= among +the modes whose =bound= is a subset of the binding. It is =None= when +no mode applies, and then the relation cannot be asked under that +binding at all. This differs from =Countable=, where the relation can +be asked but its answer never ends. =Mode.exhaustible= tells whether +the generation ends. + +* Membership +Deciding whether a tuple belongs to a relation means asking the +relation with every attribute bound. A relation is a set, so at most +one tuple agrees with a full binding, and a mode over every attribute +yields =Bounded 1=. That mode is a direct test, an intension. A +relation without one can still decide by producing the tuples under a +smaller binding and comparing each, provided that production ends, +which is deciding by extension. In principle, then, a relation decides +membership exactly when it is exhaustible with every attribute bound, +and extension and intension are the same question asked under +different bindings. + +* Reachable bindings +The principle above is wider than what RNT can do today. The +[[file:../lib/protocols/Relation.mli][Relation]] and [[file:../lib/protocols/Enumerable.mli][Enumerable]] protocols reach exactly two bindings. + +- =enumerate= produces tuples with nothing bound, so it realizes the + modes whose =bound= is empty. +- =contains= answers with every attribute bound, so it realizes the + modes whose =bound= is every attribute. + +No operation produces the tuples that agree with a partial binding. A +relation may declare a mode such as ={title; Finite}=, and +=Mode.generation= takes it into account, but nothing in RNT can act on +it. In particular, a relation cannot decide membership through a mode +with a partial binding. It decides only by its own test, or by +enumerating with nothing bound. A partial mode becomes usable once +there is an operation that produces tuples under a binding, which is +also what [[*Indexes][indexes]] need. + +* How contains decides +A substantial relation looks the tuple up by its hash. + +An ephemeral relation takes an optional membership test from whoever +builds it, through =EphemeralRelation.instantiate ?decide=. + +- With a test, =contains= uses it. +- Without one, =contains= enumerates the relation and compares, but + only when =Mode.exhaustible modes ∅= holds, meaning its generation + with nothing bound ends. +- Otherwise it fails with =undecidable-membership= and does not + enumerate. This is the case when that generation is =Countable=, or + when no mode has an empty =bound=. + +A relation that declares ={all; Bounded 1}= promises a direct test, so +whoever builds it must give it one. Nothing checks that promise yet. + +The evaluator protocol has no membership method. An ephemeral relation +generated by a program answers =contains= the way it answers =modes=. +It invokes the program, asks the relation the program returns, and +releases it. That result is a relation the evaluator built, and the +evaluator gave it whatever test it could. + +* What relations declare +A substantial relation stores its tuples in a set keyed by the hash of +each whole tuple. It declares ={∅; Finite}= for enumerating that set, +and ={all; Bounded 1}= for the lookup by hash, which needs every +attribute to compute the hash. + +An ephemeral relation that =EphemeralRelation.literal= builds from =n= +tuples declares ={∅; Bounded n}=. + +An ephemeral relation generated by a [[file:../lib/kernel/Program.ml][program]] knows its modes only from +the relation the program returns, so asking for them invokes the +program and asks the result. Invoking is lazy. An [[file:evaluators.org][evaluator]] builds +the result without reading tuples, and FOL reads tuples only when the +result is enumerated. A program with no state to run within fails with +=unbound-program=, just as reading its relation would. + +A relation that declares no modes cannot be asked under any binding. + +* Modes of a FOL plan +[[file:../lib/evaluators/FOL.ml][FOL]] computes the modes of a plan alongside its heading, before it +executes anything, and records them on each relation it derives. + +- =Base r= has the modes of =r=. +- =Project (p, A)= keeps the modes of =p= whose =bound= lies within + =A=, with the same =yields=. + +A projection never yields more tuples than its input, so =yields= +carries over. A mode that needs an attribute outside =A= no longer +applies, because a tuple of the projection holds no value for it. + +Take =book(title, author)= as a substantial relation, projected on +=title=. Asking whether ={title: "Codd"}= belongs to the projection +asks whether some book has that title, by any author. The lookup by +hash needs the author to hash the whole tuple, so the projection keeps +only ={∅; Finite}=. + +FOL gives a membership test only where it has one. The relation it +derives for =Base r= takes the =contains= of =r= as its test, so FOL +over a substantial relation still looks tuples up by hash. The relation +it derives for a projection has no test, so its =contains= enumerates +the projection, which reads all of =book=, and compares. That ends +because =book= is finite. + +A projection of a =Countable= relation stays =Countable=. FOL still +streams it, since a projection reads one tuple at a time and never +needs the end. Asking such a projection whether it holds a tuple fails +with =undecidable-membership=. + +* Indexes +An index on an attribute =a= would be a tree keyed by the value of =a= +and the hash of the tuple, and would add the mode ={a; Finite}=. A +projection that keeps =a= keeps that mode, so with one index per +attribute every membership question could stay a lookup. That takes two +things RNT does not have, the index trees and an operation that +produces tuples under a partial binding, as [[*Reachable bindings][reachable bindings]] +explains. Without the operation, the mode an index adds would be +declared and never used. + +Modes carry no cost. A lookup and a full scan both appear as an +exhaustible generation, and choosing between them belongs to whatever +plans the query. + +* Where exhaustion matters +Only a reader that runs a relation to its end needs it exhaustible. +=Relation.read= drains a relation, and admission drains every [[file:denial.org][denial]] +that holds a tuple, so both hang on a =Countable= relation. Neither +checks modes yet. Denials will check them once they are reworked. diff --git a/docs/object-system.org b/docs/object-system.org index a2f7929..facdda0 100644 --- a/docs/object-system.org +++ b/docs/object-system.org @@ -134,7 +134,7 @@ RNT exposes its resources as a hierarchy of objects. The root of this tree is an #+end_src =register= and =unregister=, as implemented by the root of the hierarchy, allows components to register resources that can be retrieved by users of the system through the corresponding =Directory= interface. The handle passed as an argument to =register= must support being "copy"ed. -As a convenience, a =path= represents a pattern of traversal the object tree. The path ="branch" @/ "master" @/ "multigroup" @/ "universe" @/ this= represents the object that can be retrieved by, from a given reference point, "open"ing the child denoted by ="branch"= on the given reference point, then "open"ing the child denoted ="master"= on the retrieved object, and so on. Every traversal assumes that the object in question has a =Directory= protocol. The ability to construct and manipulate paths is implemented by the =Path= module: +As a convenience, a =path= represents a pattern of traversal the object tree. The path ="system" @/ "branch" @/ "master" @/ "multigroup" @/ "universe" @/ this= represents the object that can be retrieved by, from a given reference point, "open"ing the child denoted by ="system"= on the given reference point, then "open"ing the child denoted ="branch"= on the retrieved object, and so on. Every traversal assumes that the object in question has a =Directory= protocol. The ability to construct and manipulate paths is implemented by the =Path= module: #+begin_src ocaml module Path : sig type path diff --git a/docs/object-taxonomy.org b/docs/object-taxonomy.org index d948845..dda3de3 100644 --- a/docs/object-taxonomy.org +++ b/docs/object-taxonomy.org @@ -21,7 +21,9 @@ may remain invariant while the extension differs between the earlier and later states. A branch identifies one such coherent state and provides the basis -from which another may be produced. It therefore gives RNT a +from which another may be produced. Each state names the one it +succeeded, under =previous=, so the history of a branch is reached by +path. It therefore gives RNT a representation of change without requiring mutation to be part of the relational semantics. How successive states are physically represented, shared, or persisted is an implementation concern. @@ -253,6 +255,10 @@ state exposed through a branch. A branch may reference multiple multigroups and determine the particular state of each that is visible from that view of the world. +Within a multigroup, relations are grouped into schemas, which for now +are only namespaces: \multigroup\[m]\schema\[s]\relation\[r]. The +canonical name of an attribute is therefore =m:s:r:a=. + * Session A session contains state specific to an interaction between a client environment and the RNT kernel. It establishes the execution context diff --git a/docs/roadmap.org b/docs/roadmap.org new file mode 100644 index 0000000..c9d14cc --- /dev/null +++ b/docs/roadmap.org @@ -0,0 +1,137 @@ +#+TITLE: Roadmap + +The work after the [[file:denial.org][denial]] rework, in the order it can be built. Each step +says why it comes where it does and what has to hold before the next +one starts. The design behind the steps is in [[file:modes.org][modes]], [[file:denial.org][denials]] and +[[file:maintenance.org][incremental maintenance]]. Each step lands with its own tests. + +* Done +- Programs are images that name their evaluator, and evaluators + =parse= and =eval= their own kinds. See [[file:evaluators.org][programs and evaluators]]. +- Ephemeral relations wrap programs, and storage keeps a program as its + evaluator name and source. +- Every relation declares its modes, and =contains= decides by a test + or by reading, never by reading without end. +- Admission refuses to judge a denial that does not end, and stops at + one witness per denial. + +* 1. Binding-aware read +Incremental checking and maintenance both ask a relation for the tuples +that agree with a partial binding, and no operation can ask that today. +A mode such as ={title; Finite}= can be declared, and nothing can act on +it. This is the [[file:modes.org::*Reachable bindings][reachable bindings]] gap, and every later step depends +on closing it. + +The step adds an operation that produces the tuples of a relation under +a binding of some of its attributes, implemented by every relation. + +- A substantial relation filters a scan until it has indexes. +- FOL passes a binding through a projection to its input when the + projection keeps the bound attributes. +- A relation that wraps a program passes the binding to the relation + the program returns. + +Membership becomes the case where every attribute is bound. + +It is done when a mode with a partial =bound= is usable, and the read +does what =Mode.generation= says it can. Two questions are open: how a +binding is represented, and what a relation does when no mode covers +the binding. Filtering a read under a smaller binding works when that +read ends, and failing as undecidable is the only other honest answer. + +* 2. Indexes +Without an index, a binding-aware read on a substantial relation still +reads the whole relation and filters it. It is correct and no faster, +and a projection still loses the lookup by hash. + +The step adds one index per attribute, a prolly tree keyed by the value +of the attribute and the hash of the tuple, written in the same +transaction as the tuple set. Each index adds the mode ={a; Finite}=, and +a projection that keeps =a= keeps it, so membership in a projection +becomes a lookup. + +It is done when a read bound on an indexed attribute reads only the +matching tuples. Two questions are open: whether indexes on combinations +of attributes are needed, and whether an index takes part in the +relation's address. It should not, since an index is derived from the +tuples. + +* 3. Read sets +RNT cannot tell which denials a commit can break, because a relation +does not say what it reads. It also cannot turn an inserted tuple into +bindings on a denial, because it does not know how the denial's +attributes line up with those of the relations it reads. + +The step has every derived relation state the relations it reads and +where each of its attributes comes from. The =provenance= field of an +attribute description is the natural place to start. + +It is done when, given the relations a commit changed, RNT lists the +denials that read them, and for each one the binding an inserted tuple +puts on it. + +* 4. Tuple deltas +The checks in the next steps start from the tuples a commit inserted and +deleted. [[file:../lib/kernel/Path.mli][Path.diff]] names the relations that changed between two states, +and stops at subtrees with equal addresses. The same walk over the two +tuple sets of a relation finds the tuples that changed inside it, since +a prolly tree shares every unchanged subtree. + +It is done when RNT gives the inserted and deleted tuples of each +relation a commit changed, at a cost proportional to the change. + +* 5. Incremental denial checking +Every commit judges every denial over the whole state. With the four +steps above, a commit can judge only what it can break. For each tuple +it inserted, it asks each denial that reads that tuple's relation for +its tuples under the binding the tuple implies, and the first tuple +found refuses the commit. Deletions never matter, because every denial +was empty before the commit. + +The full check stays for a commit that installs a denial, since a new +denial has no earlier emptiness to rely on, and for denials that +declare no usable mode for the binding. + +It is done when a commit that touches one relation reads only the bound +instances of the denials that read it, and its verdict equals the full +check's on the same states. + +* 6. Incremental maintenance +A materialized ephemeral relation would cost what a substantial one +costs to read, but every commit that changes its inputs has to change +it as well. Incremental denial checking is the insertion half of that +work. Maintenance adds the rest. + +- Materialization, so that a derived relation is stored with its tuples. +- Deletion rules, and a count per tuple of how many input tuples + produce it. +- An order for each commit: apply the change, maintain every + materialized relation that reads it, judge the denials, then move the + head. + +It is done when a maintained relation holds the same tuples as the same +relation recomputed from scratch, after every commit in the tests. + +* Alongside +These do not block the steps above. + +- =Relation.read= reads a relation whole, and a multigroup stores a + relation that wraps no program by reading it whole. Both hang on a + =Countable= relation and should check its modes first, as admission + does. +- A relation that declares ={all; Bounded 1}= promises a membership + test, and nothing checks that it gives one. A program that declares + =Finite= and never ends still hangs any reader. +- FOL detects cycles only among its own programs. A cycle across + languages recurses without end. +- Denials do not protect themselves. Removing one is judged on a state + where it is gone. This needs the access checks the [[file:../README.org][README]] plans. +- Evaluators should declare capability tags, such as =dml=, and the + registry should index them. +- Programs that change state have no result protocol. +- Once [[file:../lib/kernel/Merkle.ml][Merkle]] chunking depends only on the tuples a tree holds, + maintenance can be checked by comparing addresses instead of tuples. +- =Path.update= keeps the target it looks up and never releases it, so + each call leaks one reference to a counted namespace. This came with + the lifecycle rework and needs a decision. +- The signatures in [[file:object-system.org][the object system]] predate the code. diff --git a/images/denial-admission.svg b/images/denial-admission.svg new file mode 100644 index 0000000..80aa002 --- /dev/null +++ b/images/denial-admission.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="SEQUENCE" height="617px" preserveAspectRatio="none" style="width:878px;height:617px;background:#FFFFFF;" version="1.1" viewBox="0 0 878 617" width="878px" zoomAndPan="magnify"><?plantuml 1.2026.3?><defs/><g><rect fill="none" height="270.75" style="stroke:#000000;stroke-width:1.5;" width="655.8955" x="206.4834" y="128.5977"/><rect fill="none" height="180.8184" style="stroke:#000000;stroke-width:1.5;" width="635.8955" x="216.4834" y="182.2188"/><rect fill="none" height="120.1973" style="stroke:#000000;stroke-width:1.5;" width="615.8955" x="226.4834" y="235.8398"/><rect fill="none" height="103.8867" style="stroke:#000000;stroke-width:1.5;" width="397.9121" x="10" y="442.6582"/><g class="participant-lifeline" data-entity-uid="part1" data-qualified-name="Publish" data-source-line="1" id="part1-lifeline"><g><title>Scope.publish</title><rect fill="#000000" fill-opacity="0.00000" height="510.5684" width="8" x="70.291" y="52.9766"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="74" x2="74" y1="52.9766" y2="563.5449"/></g></g><g class="participant-lifeline" data-entity-uid="part2" data-qualified-name="BranchManager" data-source-line="2" id="part2-lifeline"><g><title>BranchManager</title><rect fill="#000000" fill-opacity="0.00000" height="510.5684" width="8" x="291.7476" y="52.9766"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="295.4834" x2="295.4834" y1="52.9766" y2="563.5449"/></g></g><g class="participant-lifeline" data-entity-uid="part3" data-qualified-name="Branch" data-source-line="3" id="part3-lifeline"><g><title>proposed</title><rect fill="#000000" fill-opacity="0.00000" height="510.5684" width="8" x="478.9839" y="52.9766"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="482.7891" x2="482.7891" y1="52.9766" y2="563.5449"/></g></g><g class="participant-lifeline" data-entity-uid="part4" data-qualified-name="Multigroup" data-source-line="4" id="part4-lifeline"><g><title>Multigroup</title><rect fill="#000000" fill-opacity="0.00000" height="510.5684" width="8" x="582.4961" y="52.9766"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="586.1787" x2="586.1787" y1="52.9766" y2="563.5449"/></g></g><g class="participant-lifeline" data-entity-uid="part5" data-qualified-name="Denial" data-source-line="5" id="part5-lifeline"><g><title>Denial</title><rect fill="#000000" fill-opacity="0.00000" height="510.5684" width="8" x="799.979" y="52.9766"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="803.5791" x2="803.5791" y1="52.9766" y2="563.5449"/></g></g><g class="participant participant-head" data-entity-uid="part1" data-qualified-name="Publish" data-source-line="1" id="part1-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="108.582" x="20" y="21.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94.582" x="27" y="42.0234">Scope.publish</text></g><g class="participant participant-tail" data-entity-uid="part1" data-qualified-name="Publish" data-source-line="1" id="part1-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="108.582" x="20" y="562.5449"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="94.582" x="27" y="583.0801">Scope.publish</text></g><g class="participant participant-head" data-entity-uid="part2" data-qualified-name="BranchManager" data-source-line="2" id="part2-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="118.5283" x="236.4834" y="21.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="104.5283" x="243.4834" y="42.0234">BranchManager</text></g><g class="participant participant-tail" data-entity-uid="part2" data-qualified-name="BranchManager" data-source-line="2" id="part2-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="118.5283" x="236.4834" y="562.5449"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="104.5283" x="243.4834" y="583.0801">BranchManager</text></g><g class="participant participant-head" data-entity-uid="part3" data-qualified-name="Branch" data-source-line="3" id="part3-head"><rect fill="#E2E2F0" height="46.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="98.3896" x="433.7891" y="5"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64.2988" x="450.8345" y="25.5352">proposed</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="84.3896" x="440.7891" y="42.0234">branch state</text></g><g class="participant participant-tail" data-entity-uid="part3" data-qualified-name="Branch" data-source-line="3" id="part3-tail"><rect fill="#E2E2F0" height="46.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="98.3896" x="433.7891" y="562.5449"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64.2988" x="450.8345" y="583.0801">proposed</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="84.3896" x="440.7891" y="599.5684">branch state</text></g><g class="participant participant-head" data-entity-uid="part4" data-qualified-name="Multigroup" data-source-line="4" id="part4-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="88.6348" x="542.1787" y="21.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74.6348" x="549.1787" y="42.0234">Multigroup</text></g><g class="participant participant-tail" data-entity-uid="part4" data-qualified-name="Multigroup" data-source-line="4" id="part4-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="88.6348" x="542.1787" y="562.5449"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74.6348" x="549.1787" y="583.0801">Multigroup</text></g><g class="participant participant-head" data-entity-uid="part5" data-qualified-name="Denial" data-source-line="5" id="part5-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="56.7998" x="775.5791" y="21.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="42.7998" x="782.5791" y="42.0234">Denial</text></g><g class="participant participant-tail" data-entity-uid="part5" data-qualified-name="Denial" data-source-line="5" id="part5-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="56.7998" x="775.5791" y="562.5449"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="42.7998" x="782.5791" y="583.0801">Denial</text></g><g class="message" data-entity-1="part1" data-entity-2="part2" data-source-line="7" id="msg1"><polygon fill="#181818" points="283.7476,80.2871,293.7476,84.2871,283.7476,88.2871,287.7476,84.2871" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="74.291" x2="289.7476" y1="84.2871" y2="84.2871"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="93.1646" x="81.291" y="79.5449">move the head</text></g><g class="message" data-entity-1="part2" data-entity-2="part3" data-source-line="8" id="msg2"><polygon fill="#181818" points="470.9839,109.5977,480.9839,113.5977,470.9839,117.5977,474.9839,113.5977" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="295.7476" x2="476.9839" y1="113.5977" y2="113.5977"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36.2261" x="302.7476" y="108.8555">check</text></g><path d="M206.4834,128.5977 L280.9302,128.5977 L280.9302,135.9082 L270.9302,145.9082 L206.4834,145.9082 L206.4834,128.5977" fill="#EEEEEE" style="stroke:#000000;stroke-width:1.5;"/><rect fill="none" height="270.75" style="stroke:#000000;stroke-width:1.5;" width="655.8955" x="206.4834" y="128.5977"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="700" lengthAdjust="spacing" textLength="29.4468" x="221.4834" y="142.166">loop</text><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="101.0679" x="295.9302" y="141.2324">[each multigroup]</text><g class="message" data-entity-1="part3" data-entity-2="part4" data-source-line="10" id="msg3"><polygon fill="#181818" points="574.4961,163.2188,584.4961,167.2188,574.4961,171.2188,578.4961,167.2188" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="482.9839" x2="580.4961" y1="167.2188" y2="167.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="36.2261" x="489.9839" y="162.4766">check</text></g><path d="M216.4834,182.2188 L290.9302,182.2188 L290.9302,189.5293 L280.9302,199.5293 L216.4834,199.5293 L216.4834,182.2188" fill="#EEEEEE" style="stroke:#000000;stroke-width:1.5;"/><rect fill="none" height="180.8184" style="stroke:#000000;stroke-width:1.5;" width="635.8955" x="216.4834" y="182.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="700" lengthAdjust="spacing" textLength="29.4468" x="231.4834" y="195.7871">loop</text><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="72.3486" x="305.9302" y="194.8535">[each denial]</text><g class="message" data-entity-1="part4" data-entity-2="part5" data-source-line="12" id="msg4"><polygon fill="#181818" points="791.979,216.8398,801.979,220.8398,791.979,224.8398,795.979,220.8398" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="586.4961" x2="797.979" y1="220.8398" y2="220.8398"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="42.1738" x="593.4961" y="216.0977">modes</text></g><path d="M226.4834,235.8398 L288.6221,235.8398 L288.6221,243.1504 L278.6221,253.1504 L226.4834,253.1504 L226.4834,235.8398" fill="#EEEEEE" style="stroke:#000000;stroke-width:1.5;"/><rect fill="none" height="120.1973" style="stroke:#000000;stroke-width:1.5;" width="615.8955" x="226.4834" y="235.8398"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="700" lengthAdjust="spacing" textLength="17.1387" x="241.4834" y="249.4082">alt</text><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="211.5781" x="303.6221" y="248.4746">[not exhaustible with nothing bound]</text><g class="message" data-entity-1="part4" data-entity-2="part2" data-source-line="14" id="msg5"><polygon fill="#181818" points="306.7476,270.4609,296.7476,274.4609,306.7476,278.4609,302.7476,274.4609" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="300.7476" x2="585.4961" y1="274.4609" y2="274.4609"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="123.7603" x="312.7476" y="269.7188">unjudgeable-denial</text></g><line style="stroke:#000000;stroke-width:1;stroke-dasharray:2,2;" x1="226.4834" x2="842.3789" y1="283.4609" y2="283.4609"/><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="73.5786" x="231.4834" y="294.0957">[exhaustible]</text><g class="message" data-entity-1="part4" data-entity-2="part5" data-source-line="16" id="msg6"><polygon fill="#181818" points="791.979,314.7266,801.979,318.7266,791.979,322.7266,795.979,318.7266" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="586.4961" x2="797.979" y1="318.7266" y2="318.7266"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="193.4829" x="593.4961" y="313.9844">enumerate, read the first tuple</text></g><g class="message" data-entity-1="part5" data-entity-2="part4" data-source-line="17" id="msg7"><polygon fill="#181818" points="597.4961,344.0371,587.4961,348.0371,597.4961,352.0371,593.4961,348.0371" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="591.4961" x2="802.979" y1="348.0371" y2="348.0371"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="133.0532" x="603.4961" y="343.2949">a witness, or nothing</text></g><g class="message" data-entity-1="part4" data-entity-2="part3" data-source-line="20" id="msg8"><polygon fill="#181818" points="493.9839,387.3477,483.9839,391.3477,493.9839,395.3477,489.9839,391.3477" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="487.9839" x2="585.4961" y1="391.3477" y2="391.3477"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="60.709" x="499.9839" y="386.6055">violations</text></g><g class="message" data-entity-1="part3" data-entity-2="part2" data-source-line="22" id="msg9"><polygon fill="#181818" points="306.7476,423.6582,296.7476,427.6582,306.7476,431.6582,302.7476,427.6582" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="300.7476" x2="481.9839" y1="427.6582" y2="427.6582"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="163.2363" x="312.7476" y="422.916">violations, named by path</text></g><path d="M10,442.6582 L72.1387,442.6582 L72.1387,449.9688 L62.1387,459.9688 L10,459.9688 L10,442.6582" fill="#EEEEEE" style="stroke:#000000;stroke-width:1.5;"/><rect fill="none" height="103.8867" style="stroke:#000000;stroke-width:1.5;" width="397.9121" x="10" y="442.6582"/><text fill="#000000" font-family="sans-serif" font-size="13" font-weight="700" lengthAdjust="spacing" textLength="17.1387" x="25" y="456.2266">alt</text><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="36.0723" x="87.1387" y="455.293">[none]</text><g class="message" data-entity-1="part2" data-entity-2="part2" data-source-line="24" id="msg10"><line style="stroke:#181818;stroke-width:1;" x1="295.7476" x2="337.7476" y1="481.2793" y2="481.2793"/><line style="stroke:#181818;stroke-width:1;" x1="337.7476" x2="337.7476" y1="481.2793" y2="494.2793"/><line style="stroke:#181818;stroke-width:1;" x1="296.7476" x2="337.7476" y1="494.2793" y2="494.2793"/><polygon fill="#181818" points="306.7476,490.2793,296.7476,494.2793,306.7476,498.2793,302.7476,494.2793" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="93.1646" x="302.7476" y="476.5371">move the head</text></g><line style="stroke:#000000;stroke-width:1;stroke-dasharray:2,2;" x1="10" x2="407.9121" y1="503.2793" y2="503.2793"/><text fill="#000000" font-family="sans-serif" font-size="11" font-weight="700" lengthAdjust="spacing" textLength="38.5107" x="15" y="513.9141">[some]</text><g class="message" data-entity-1="part2" data-entity-2="part1" data-source-line="26" id="msg11"><polygon fill="#181818" points="85.291,534.5449,75.291,538.5449,85.291,542.5449,81.291,538.5449" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="79.291" x2="294.7476" y1="538.5449" y2="538.5449"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="197.4565" x="91.291" y="533.8027">refused, one witness per denial</text></g><?plantuml-src TPAzJiGm48JxFCNgwXmFS0K5eZq9YPPc4-_51cTj-UU0jsVFlln1I155pZVhcR4zXcHwuv4J75vxv-d1vywQg0-04LxgT-DNs5D0xlKP6GSAcpy77vntaTGRTmM2c31HcLHL6_oSRJ93SDblbf-93TgckUyDnyVjbd22qLq9aYRGXAhP_frnuNfD_KTZdVD0A2lZiYDCbF2BaLa3iAXKDGJoh4ZHLQV5Zw9O0BG9s2MWBuqv9jDPWa-Jz6rH6nwWSvbLGRVJTb9cVizg89GPnvK7if7MytU6hQmHvv62d48BGSeghLrCY0bIzhFuZXz_bu4tyqmnjk32bA6wA2dgUpU7gA_6MKp6SMnku5JuJjI5R85n90NTDtXCkgc5Ciq7zkUS_xiMfQVenXsfQAVRTP9QBbdkh2HackA25-pUkLZ_0G00?></g></svg>
\ No newline at end of file diff --git a/images/evaluators-classes.svg b/images/evaluators-classes.svg new file mode 100644 index 0000000..10c2aa7 --- /dev/null +++ b/images/evaluators-classes.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="CLASS" height="528px" preserveAspectRatio="none" style="width:682px;height:528px;background:#FFFFFF;" version="1.1" viewBox="0 0 682 528" width="682px" zoomAndPan="magnify"><?plantuml 1.2026.3?><defs/><g><!--class Program--><g class="entity" data-qualified-name="Program" data-source-line="4" id="ent0002"><rect fill="#F1F1F1" height="98.0859" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="146.5039" x="295.92" y="7"/><ellipse cx="336.65" cy="27.3105" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M339.1231,33.4536 Q338.5421,33.7524 337.9029,33.9019 Q337.2638,34.0513 336.5582,34.0513 Q334.0513,34.0513 332.7315,32.3994 Q331.4117,30.7476 331.4117,27.6265 Q331.4117,24.4971 332.7315,22.8452 Q334.0513,21.1934 336.5582,21.1934 Q337.2638,21.1934 337.9112,21.3428 Q338.5587,21.4922 339.1231,21.791 L339.1231,24.5137 Q338.4923,23.9326 337.8988,23.6628 Q337.3053,23.3931 336.6744,23.3931 Q335.3297,23.3931 334.6449,24.4597 Q333.96,25.5264 333.96,27.6265 Q333.96,29.7183 334.6449,30.7849 Q335.3297,31.8516 336.6744,31.8516 Q337.3053,31.8516 337.8988,31.5818 Q338.4923,31.312 339.1231,30.731 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="12" font-style="italic" lengthAdjust="spacing" textLength="48.041" x="361.0103" y="23.6016">«image»</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="57.3262" x="356.3678" y="39.668">Program</text><line style="stroke:#181818;stroke-width:0.5;" x1="296.92" x2="441.4239" y1="47.6211" y2="47.6211"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="115.6641" x="301.92" y="65.1563">evaluator : string</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134.5039" x="301.92" y="81.6445">kind : Program.kind</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="97.9795" x="301.92" y="98.1328">source : string</text></g><!--class Executable--><g class="entity" data-qualified-name="Executable" data-source-line="9" id="ent0003"><rect fill="#F1F1F1" height="56.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="223.7979" x="443.27" y="205.33"/><ellipse cx="514.1894" cy="221.33" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M510.6172,217.5951 L510.6172,215.4369 L517.9966,215.4369 L517.9966,217.5951 L515.5312,217.5951 L515.5312,225.6718 L517.9966,225.6718 L517.9966,227.83 L510.6172,227.83 L510.6172,225.6718 L513.0825,225.6718 L513.0825,217.5951 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="73.459" x="534.6894" y="226.621">Executable</text><line style="stroke:#181818;stroke-width:0.5;" x1="444.27" x2="666.0679" y1="237.33" y2="237.33"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="211.7979" x="449.27" y="254.8652">invoke(inputs, within) : relation</text></g><!--class Evaluator--><g class="entity" data-qualified-name="Evaluator" data-source-line="12" id="ent0004"><rect fill="#F1F1F1" height="72.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="244.6475" x="54.84" y="197.09"/><ellipse cx="141.6154" cy="213.09" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M138.0431,209.3551 L138.0431,207.1969 L145.4225,207.1969 L145.4225,209.3551 L142.9572,209.3551 L142.9572,217.4318 L145.4225,217.4318 L145.4225,219.59 L138.0431,219.59 L138.0431,217.4318 L140.5085,217.4318 L140.5085,209.3551 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="62.5967" x="162.1154" y="218.381">Evaluator</text><line style="stroke:#181818;stroke-width:0.5;" x1="55.84" x2="298.4875" y1="229.09" y2="229.09"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134.4561" x="60.84" y="246.6252">parse(source) : kind</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="232.6475" x="60.84" y="263.1134">eval(kind, inputs, within) : relation</text></g><!--class FOL--><g class="entity" data-qualified-name="FOL" data-source-line="16" id="ent0005"><rect fill="#F1F1F1" height="32" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="125.085" x="114.62" y="40.04"/><ellipse cx="129.62" cy="56.04" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M132.0931,62.1831 Q131.5121,62.4819 130.8729,62.6313 Q130.2338,62.7807 129.5282,62.7807 Q127.0214,62.7807 125.7015,61.1289 Q124.3817,59.477 124.3817,56.3559 Q124.3817,53.2265 125.7015,51.5747 Q127.0214,49.9228 129.5282,49.9228 Q130.2338,49.9228 130.8812,50.0722 Q131.5287,50.2216 132.0931,50.5205 L132.0931,53.2431 Q131.4623,52.6621 130.8688,52.3923 Q130.2753,52.1225 129.6444,52.1225 Q128.2997,52.1225 127.6149,53.1892 Q126.9301,54.2558 126.9301,56.3559 Q126.9301,58.4477 127.6149,59.5144 Q128.2997,60.581 129.6444,60.581 Q130.2753,60.581 130.8688,60.3112 Q131.4623,60.0415 132.0931,59.4604 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93.085" x="143.62" y="61.331">FOL evaluator</text></g><!--class EphemeralRelation--><g class="entity" data-qualified-name="EphemeralRelation" data-source-line="17" id="ent0006"><rect fill="#F1F1F1" height="32" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="158.2666" x="194.03" y="347.06"/><ellipse cx="209.03" cy="363.06" fill="#ADD1B2" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M211.5031,369.2031 Q210.9221,369.5019 210.2829,369.6513 Q209.6438,369.8007 208.9382,369.8007 Q206.4314,369.8007 205.1115,368.1489 Q203.7917,366.497 203.7917,363.3759 Q203.7917,360.2465 205.1115,358.5947 Q206.4314,356.9428 208.9382,356.9428 Q209.6438,356.9428 210.2912,357.0922 Q210.9387,357.2416 211.5031,357.5405 L211.5031,360.2631 Q210.8723,359.6821 210.2788,359.4123 Q209.6853,359.1425 209.0544,359.1425 Q207.7097,359.1425 207.0249,360.2092 Q206.3401,361.2758 206.3401,363.3759 Q206.3401,365.4677 207.0249,366.5344 Q207.7097,367.601 209.0544,367.601 Q209.6853,367.601 210.2788,367.3312 Q210.8723,367.0615 211.5031,366.4804 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="126.2666" x="223.03" y="368.351">EphemeralRelation</text></g><!--class Relation--><g class="entity" data-qualified-name="Relation" data-source-line="18" id="ent0007"><rect fill="#F1F1F1" height="72.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="156.334" x="7" y="440.06"/><ellipse cx="53.416" cy="456.06" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M49.8438,452.3251 L49.8438,450.1669 L57.2231,450.1669 L57.2231,452.3251 L54.7578,452.3251 L54.7578,460.4018 L57.2231,460.4018 L57.2231,462.56 L49.8438,462.56 L49.8438,460.4018 L52.3091,460.4018 L52.3091,452.3251 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="55.002" x="73.916" y="461.351">Relation</text><line style="stroke:#181818;stroke-width:0.5;" x1="8" x2="162.334" y1="472.06" y2="472.06"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="144.334" x="13" y="489.5952">contains(tuple) : bool</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="114.748" x="13" y="506.0834">modes() : Mode.t</text></g><!--class Enumerable--><g class="entity" data-qualified-name="Enumerable" data-source-line="22" id="ent0008"><rect fill="#F1F1F1" height="56.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="149.9902" x="198.17" y="448.31"/><ellipse cx="230.2867" cy="464.31" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M226.7144,460.5751 L226.7144,458.4169 L234.0938,458.4169 L234.0938,460.5751 L231.6285,460.5751 L231.6285,468.6518 L234.0938,468.6518 L234.0938,470.81 L226.7144,470.81 L226.7144,468.6518 L229.1798,468.6518 L229.1798,460.5751 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="79.9531" x="248.0904" y="469.601">Enumerable</text><line style="stroke:#181818;stroke-width:0.5;" x1="199.17" x2="347.1602" y1="480.31" y2="480.31"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="137.9902" x="204.17" y="497.8452">enumerate() : cursor</text></g><!--class Schematics--><g class="entity" data-qualified-name="Schematics" data-source-line="25" id="ent0009"><rect fill="#F1F1F1" height="56.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="146.1963" x="383.07" y="448.31"/><ellipse cx="415.442" cy="464.31" fill="#B4A7E5" rx="11" ry="11" style="stroke:#181818;stroke-width:1;"/><path d="M411.8698,460.5751 L411.8698,458.4169 L419.2492,458.4169 L419.2492,460.5751 L416.7838,460.5751 L416.7838,468.6518 L419.2492,468.6518 L419.2492,470.81 L411.8698,470.81 L411.8698,468.6518 L414.3351,468.6518 L414.3351,460.5751 Z " fill="#000000"/><text fill="#000000" font-family="sans-serif" font-size="14" font-style="italic" lengthAdjust="spacing" textLength="75.5918" x="433.3025" y="469.601">Schematics</text><line style="stroke:#181818;stroke-width:0.5;" x1="384.07" x2="528.2663" y1="480.31" y2="480.31"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="134.1963" x="389.07" y="497.8452">describe() : heading</text></g><!--link Program to Executable--><g class="link" data-entity-1="ent0002" data-entity-2="ent0003" data-link-type="extension" data-source-line="29" id="lnk10"><path codeLine="29" d="M420.48,105.47 C454.46,137.54 484.7085,166.0864 512.9185,192.7064" fill="none" id="Program-to-Executable" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="526.01,205.06,517.0364,188.3425,508.8006,197.0702,526.01,205.06" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="145.1646" x="485.3" y="156.1584">runs when given a root</text></g><!--link FOL to Evaluator--><g class="link" data-entity-1="ent0005" data-entity-2="ent0004" data-link-type="extension" data-source-line="30" id="lnk11"><path codeLine="30" d="M177.17,72.31 C177.17,99.84 177.17,140.46 177.17,178.66" fill="none" id="FOL-to-Evaluator" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="177.17,196.66,183.17,178.66,171.17,178.66,177.17,196.66" style="stroke:#181818;stroke-width:1;"/></g><!--link Program to Evaluator--><g class="link" data-entity-1="ent0002" data-entity-2="ent0004" data-link-type="dependency" data-source-line="31" id="lnk12"><path codeLine="31" d="M295.42,95.53 C277.23,106.89 258.57,120.29 243.17,135.09 C224.44,153.07 211.1185,172.044 199.3585,191.804" fill="none" id="Program-to-Evaluator" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="196.29,196.96,204.3301,191.2717,198.8471,192.6634,197.4555,187.1803,196.29,196.96" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="71.9063" x="257.4747" y="148.6584">names it as</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="98.5156" x="244.17" y="163.9689">scope:language</text></g><!--link EphemeralRelation to Relation--><g class="link" data-entity-1="ent0006" data-entity-2="ent0007" data-link-type="extension" data-source-line="32" id="lnk13"><path codeLine="32" d="M247.42,379.33 C221.37,394.78 195.6428,410.0394 161.2728,430.4194" fill="none" id="EphemeralRelation-to-Relation" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="145.79,439.6,164.333,435.5803,158.2125,425.2584,145.79,439.6" style="stroke:#181818;stroke-width:1;"/></g><!--link EphemeralRelation to Enumerable--><g class="link" data-entity-1="ent0006" data-entity-2="ent0008" data-link-type="extension" data-source-line="33" id="lnk14"><path codeLine="33" d="M273.17,379.1 C273.17,396.69 273.17,408.18 273.17,429.93" fill="none" id="EphemeralRelation-to-Enumerable" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="273.17,447.93,279.17,429.93,267.17,429.93,273.17,447.93" style="stroke:#181818;stroke-width:1;"/></g><!--link EphemeralRelation to Schematics--><g class="link" data-entity-1="ent0006" data-entity-2="ent0009" data-link-type="extension" data-source-line="34" id="lnk15"><path codeLine="34" d="M298.23,379.33 C327.11,396.92 359.7669,416.8268 395.2969,438.4668" fill="none" id="EphemeralRelation-to-Schematics" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="none" points="410.67,447.83,398.418,433.3425,392.1759,443.5912,410.67,447.83" style="stroke:#181818;stroke-width:1;"/></g><!--link EphemeralRelation to Program--><g class="link" data-entity-1="ent0006" data-entity-2="ent0002" data-link-type="aggregation" data-source-line="35" id="lnk16"><path codeLine="35" d="M303.5423,339.0243 C312.8723,331.1743 313.92,328.58 320.17,317.06 C356.61,249.82 366.1196,166.0543 368.4896,111.5043" fill="none" id="EphemeralRelation-Program" style="stroke:#181818;stroke-width:1;"/><polygon fill="none" points="294.36,346.75,301.5264,345.9479,303.5423,339.0243,296.3759,339.8264,294.36,346.75" style="stroke:#181818;stroke-width:1;"/><polygon fill="#181818" points="368.75,105.51,364.3631,114.3279,368.533,110.5053,372.3556,114.6751,368.75,105.51" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="37.3242" x="359.49" y="238.6484">wraps</text><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="24.667" x="343.1256" y="125.7542">0..1</text></g><!--link Evaluator to EphemeralRelation--><g class="link" data-entity-1="ent0004" data-entity-2="ent0006" data-link-type="dependency" data-source-line="36" id="lnk17"><path codeLine="36" d="M204.16,270.43 C223.13,295.61 243.9418,323.2461 258.0618,342.0061" fill="none" id="Evaluator-to-EphemeralRelation" style="stroke:#181818;stroke-width:1;stroke-dasharray:7,7;"/><polygon fill="#181818" points="261.67,346.8,259.4536,337.2038,258.6632,342.8051,253.0618,342.0147,261.67,346.8" style="stroke:#181818;stroke-width:1;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="74.5278" x="237.59" y="313.6284">eval returns</text></g><?plantuml-src VL9DJyCm3BttLrWx5Gagk4xJ90uW8O50SENYPQQDrYPLx6ny_dUSTcsPO5ogvldvvVavbLqJKDFA6pJKh2XmnXlhMWpOWAcH-L8as5KKkZ7UFTbtWlCiwohm47oPU8k5RR2avH8-CW3QOXrHV80viFQwKa7LNEjvtr6aewBiOp0qyRuowuJ22ofuzKec2gvgwbIjs_eDvMej2f_2pafbtOcs1gfHh7U7pQE7rAlJCENzPQbbVtiocgV_K_XNjXzsTdr_Eyqs0sHGP5-yQYjgA63zEBHDNWQeiw8P2bh7kSIsxioilA-rqFWrSPw0E_qhv70S5vFwa0NrHw6ERs9W7mxuJqRjwAM6Exug6zrXJwy8rttMsR2_elXS_inRfu-EOLUHWz9kzOiGl9SiHT2JXnn6ZREp7wWgE6o8mOhczEpO-9RcDReowZl9VgNLYuxP7QbFAHnZJ7F_mV39ukoyA2vcuykTmovWg-pHUL4iVszJUMdp-YWa1iVV?></g></svg>
\ No newline at end of file diff --git a/images/evaluators-reading.svg b/images/evaluators-reading.svg new file mode 100644 index 0000000..29917bb --- /dev/null +++ b/images/evaluators-reading.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" data-diagram-type="SEQUENCE" height="565px" preserveAspectRatio="none" style="width:1056px;height:565px;background:#FFFFFF;" version="1.1" viewBox="0 0 1056 565" width="1056px" zoomAndPan="magnify"><?plantuml 1.2026.3?><defs/><g><g class="participant-lifeline" data-entity-uid="part1" data-qualified-name="Client" data-source-line="1" id="part1-lifeline"><g><title>Client</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="23.7524" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="27" x2="27" y1="81.4883" y2="482.5254"/></g></g><g class="participant-lifeline" data-entity-uid="part2" data-qualified-name="Path" data-source-line="2" id="part2-lifeline"><g><title>Path.lookup</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="264.6138" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="268.2373" x2="268.2373" y1="81.4883" y2="482.5254"/></g></g><g class="participant-lifeline" data-entity-uid="part3" data-qualified-name="Multigroup" data-source-line="3" id="part3-lifeline"><g><title>Multigroup</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="366.3076" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="369.9902" x2="369.9902" y1="81.4883" y2="482.5254"/></g></g><g class="participant-lifeline" data-entity-uid="part4" data-qualified-name="E" data-source-line="4" id="part4-lifeline"><g><title>EphemeralRelation</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="636" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="639.8667" x2="639.8667" y1="81.4883" y2="482.5254"/></g></g><g class="participant-lifeline" data-entity-uid="part5" data-qualified-name="Program" data-source-line="5" id="part5-lifeline"><g><title>Program</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="834.7192" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="838.0562" x2="838.0562" y1="81.4883" y2="482.5254"/></g></g><g class="participant-lifeline" data-entity-uid="part6" data-qualified-name="Ev" data-source-line="6" id="part6-lifeline"><g><title>evaluator under</title><rect fill="#000000" fill-opacity="0.00000" height="401.0371" width="8" x="966.2104" y="81.4883"/><line style="stroke:#181818;stroke-width:0.5;stroke-dasharray:5,5;" x1="969.9023" x2="969.9023" y1="81.4883" y2="482.5254"/></g></g><g class="participant participant-head" data-entity-uid="part1" data-qualified-name="Client" data-source-line="1" id="part1-head"><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="39.5049" x="5" y="78.5352">Client</text><ellipse cx="27.7524" cy="13.5" fill="#E2E2F0" rx="8" ry="8" style="stroke:#181818;stroke-width:0.5;"/><path d="M27.7524,21.5 L27.7524,48.5 M14.7524,29.5 L40.7524,29.5 M27.7524,48.5 L14.7524,63.5 M27.7524,48.5 L40.7524,63.5" fill="none" style="stroke:#181818;stroke-width:0.5;"/></g><g class="participant participant-tail" data-entity-uid="part1" data-qualified-name="Client" data-source-line="1" id="part1-tail"><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="39.5049" x="5" y="495.0605">Client</text><ellipse cx="27.7524" cy="506.5137" fill="#E2E2F0" rx="8" ry="8" style="stroke:#181818;stroke-width:0.5;"/><path d="M27.7524,514.5137 L27.7524,541.5137 M14.7524,522.5137 L40.7524,522.5137 M27.7524,541.5137 L14.7524,556.5137 M27.7524,541.5137 L40.7524,556.5137" fill="none" style="stroke:#181818;stroke-width:0.5;"/></g><g class="participant participant-head" data-entity-uid="part2" data-qualified-name="Path" data-source-line="2" id="part2-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="94.7529" x="221.2373" y="50"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80.7529" x="228.2373" y="70.5352">Path.lookup</text></g><g class="participant participant-tail" data-entity-uid="part2" data-qualified-name="Path" data-source-line="2" id="part2-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="94.7529" x="221.2373" y="481.5254"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80.7529" x="228.2373" y="502.0605">Path.lookup</text></g><g class="participant participant-head" data-entity-uid="part3" data-qualified-name="Multigroup" data-source-line="3" id="part3-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="88.6348" x="325.9902" y="50"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74.6348" x="332.9902" y="70.5352">Multigroup</text></g><g class="participant participant-tail" data-entity-uid="part3" data-qualified-name="Multigroup" data-source-line="3" id="part3-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="88.6348" x="325.9902" y="481.5254"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="74.6348" x="332.9902" y="502.0605">Multigroup</text></g><g class="participant participant-head" data-entity-uid="part4" data-qualified-name="E" data-source-line="4" id="part4-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="140.2666" x="569.8667" y="50"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="126.2666" x="576.8667" y="70.5352">EphemeralRelation</text></g><g class="participant participant-tail" data-entity-uid="part4" data-qualified-name="E" data-source-line="4" id="part4-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="140.2666" x="569.8667" y="481.5254"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="126.2666" x="576.8667" y="502.0605">EphemeralRelation</text></g><g class="participant participant-head" data-entity-uid="part5" data-qualified-name="Program" data-source-line="5" id="part5-head"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="71.3262" x="803.0562" y="50"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="57.3262" x="810.0562" y="70.5352">Program</text></g><g class="participant participant-tail" data-entity-uid="part5" data-qualified-name="Program" data-source-line="5" id="part5-tail"><rect fill="#E2E2F0" height="30.4883" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="71.3262" x="803.0562" y="481.5254"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="57.3262" x="810.0562" y="502.0605">Program</text></g><g class="participant participant-head" data-entity-uid="part6" data-qualified-name="Ev" data-source-line="6" id="part6-head"><rect fill="#E2E2F0" height="46.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="150.6162" x="894.9023" y="33.5117"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106.9551" x="916.7329" y="54.0469">evaluator under</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="136.6162" x="901.9023" y="70.5352">the program's name</text></g><g class="participant participant-tail" data-entity-uid="part6" data-qualified-name="Ev" data-source-line="6" id="part6-tail"><rect fill="#E2E2F0" height="46.9766" rx="2.5" ry="2.5" style="stroke:#181818;stroke-width:0.5;" width="150.6162" x="894.9023" y="481.5254"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106.9551" x="916.7329" y="502.0605">evaluator under</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="136.6162" x="901.9023" y="518.5488">the program's name</text></g><g class="message" data-entity-1="part1" data-entity-2="part2" data-source-line="8" id="msg1"><polygon fill="#181818" points="256.6138,108.7988,266.6138,112.7988,256.6138,116.7988,260.6138,112.7988" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="27.7524" x2="262.6138" y1="112.7988" y2="112.7988"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="216.8613" x="34.7524" y="108.0566">lookup a relation in a branch state</text></g><g class="message" data-entity-1="part2" data-entity-2="part3" data-source-line="9" id="msg2"><polygon fill="#181818" points="358.3076,138.1094,368.3076,142.1094,358.3076,146.1094,362.3076,142.1094" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="268.6138" x2="364.3076" y1="142.1094" y2="142.1094"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="37.521" x="275.6138" y="137.3672">find it</text></g><g class="message" data-entity-1="part3" data-entity-2="part4" data-source-line="10" id="msg3"><polygon fill="#181818" points="628,167.4199,638,171.4199,628,175.4199,632,171.4199" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="370.3076" x2="634" y1="171.4199" y2="171.4199"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="245.6924" x="377.3076" y="166.6777">load (evaluator name, source, heading)</text></g><g class="message" data-entity-1="part4" data-entity-2="part5" data-source-line="11" id="msg4"><polygon fill="#181818" points="826.7192,196.7305,836.7192,200.7305,826.7192,204.7305,830.7192,200.7305" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="640" x2="832.7192" y1="200.7305" y2="200.7305"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="114.7148" x="647" y="195.9883">Program.load root</text></g><g class="message" data-entity-1="part5" data-entity-2="part6" data-source-line="12" id="msg5"><polygon fill="#181818" points="958.2104,226.041,968.2104,230.041,958.2104,234.041,962.2104,230.041" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="838.7192" x2="964.2104" y1="230.041" y2="230.041"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="80.5645" x="845.7192" y="225.2988">parse source</text></g><g class="message" data-entity-1="part6" data-entity-2="part5" data-source-line="13" id="msg6"><polygon fill="#181818" points="849.7192,255.3516,839.7192,259.3516,849.7192,263.3516,845.7192,259.3516" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="843.7192" x2="969.2104" y1="259.3516" y2="259.3516"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="27.606" x="855.7192" y="254.6094">kind</text></g><g class="message" data-entity-1="part4" data-entity-2="part1" data-source-line="14" id="msg7"><polygon fill="#181818" points="38.7524,284.6621,28.7524,288.6621,38.7524,292.6621,34.7524,288.6621" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="32.7524" x2="639" y1="288.6621" y2="288.6621"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="193.7241" x="44.7524" y="283.9199">relation wrapping the program</text></g><g class="message" data-entity-1="part1" data-entity-2="part4" data-source-line="15" id="msg8"><polygon fill="#181818" points="628,313.9727,638,317.9727,628,321.9727,632,317.9727" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="27.7524" x2="634" y1="317.9727" y2="317.9727"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="67.3613" x="34.7524" y="313.2305">enumerate</text></g><g class="message" data-entity-1="part4" data-entity-2="part5" data-source-line="16" id="msg9"><polygon fill="#181818" points="826.7192,343.2832,836.7192,347.2832,826.7192,351.2832,830.7192,347.2832" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="640" x2="832.7192" y1="347.2832" y2="347.2832"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="174.7192" x="647" y="342.541">invoke ~within:branch state</text></g><g class="message" data-entity-1="part5" data-entity-2="part6" data-source-line="17" id="msg10"><polygon fill="#181818" points="958.2104,372.5938,968.2104,376.5938,958.2104,380.5938,962.2104,376.5938" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;" x1="838.7192" x2="964.2104" y1="376.5938" y2="376.5938"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="107.4912" x="845.7192" y="371.8516">eval kind ~within</text></g><g class="message" data-entity-1="part6" data-entity-2="part5" data-source-line="18" id="msg11"><polygon fill="#181818" points="849.7192,401.9043,839.7192,405.9043,849.7192,409.9043,845.7192,405.9043" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="843.7192" x2="969.2104" y1="405.9043" y2="405.9043"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="88.1626" x="855.7192" y="401.1621">result relation</text></g><g class="message" data-entity-1="part5" data-entity-2="part4" data-source-line="19" id="msg12"><polygon fill="#181818" points="651,431.2148,641,435.2148,651,439.2148,647,435.2148" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="645" x2="837.7192" y1="435.2148" y2="435.2148"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="88.1626" x="657" y="430.4727">result relation</text></g><g class="message" data-entity-1="part4" data-entity-2="part1" data-source-line="20" id="msg13"><polygon fill="#181818" points="38.7524,460.5254,28.7524,464.5254,38.7524,468.5254,34.7524,464.5254" style="stroke:#181818;stroke-width:1;"/><line style="stroke:#181818;stroke-width:1;stroke-dasharray:2,2;" x1="32.7524" x2="639" y1="464.5254" y2="464.5254"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="135.6431" x="44.7524" y="459.7832">cursor over the result</text></g><?plantuml-src PP2zRiCm38HtFGMHfItGv04yT2ay5WWoTs5jDXRYI095ELkVlPHirp-TRDDtv7U7ZNY6zzwI4nEGnJOse1Cud56wK-_zBOK3O8JylL5yf5xibNqAct4TEheJOt-X7iLwbytrHd9cVsMyR-_HW7t2J9DSI_pff2C8e_8fWiCx5OvwC6RaXUDReO8AHav0uFcerRlmnUYQ3gAWa2bIjIpSQloshWKhPZLKILrMOWlF2rK6U8Ne4pVwx0XRwwulfYuK8wQwfhTJSRFtOkP_UUsW2WqTQTfZT7BS-6_AatVgS0fPBPaUZ27eLLXLi-eYKvDBkNoDkmEpRl0tWf-7bSwwQjlC3Z67BYIp_3ydKzJ6_j2M5HF7_lykKfCuQgT-82vXHlal?></g></svg>
\ No newline at end of file |
