#+TITLE: Evaluators * 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. 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. The FOL evaluator is therefore, one of such evaluators that is shipped alongside RNT for specifically Data Retrieval. * 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). #+begin_src ocaml module Make (P : PROGRAM) : sig class type implementation = object method invoke : P.t -> (Handle.t, condition) result end end #+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: #+begin_src ocaml type plan = Base of Handle.t #+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 #+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. Cancellation, budgets, and resource accounting are not implemented. * 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. * 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.