Chapter 23: jev-facts — everything a rule is waiting on, in one envelope
Suppose you are deciding what to do with something, and your decision is a set of rules: if it is safe and it only reads, let it through; if it deletes, ask first. Each rule stands on facts (is it safe? does it read? does it delete?), and the facts are things only Jev can tell you.
The slow way is to ask as you go. Rule one wants "safe", so you ask Jev and wait. It also wants "reads", so you ask again and wait again. Three rules, five facts, five round trips.
A rules engine that knows its rules can do better. Before it asks anything, it can look at every rule still in the running and collect every fact any of them is waiting on. Jev answers many questions in one request for the price of one wait. So the engine says "I want these five", and this crate puts five questions in one envelope, sends nothing itself, and when the answers come back, reads each one as the fact it teaches.
That is all jev-facts is: the piece between "these facts are wanted" and
"these facts are known".
sequenceDiagram participant E as A rules engine (rete, or your own) participant F as jev-facts participant J as Jev E->>F: wanted(facts): safe, reads, deletes, load F-->>E: Prepared: one request, three questions E->>J: the request (sent however the host sends) J-->>E: the response E->>F: prepared.judged(response), then learn(...) F-->>E: safe = no, reads = no, deletes = yes, load = 0.3
Four facts, three questions. That is not a mistake: "reads" and "deletes" are two readings of one question, "what does the command do?". They share an id, the question is asked once, and each fact takes its own value out of the one answer.
A host says what its facts are by implementing one trait:
impl jev_facts::Source for Command {
type Fact = Fact;
type Value = Value;
fn question_id(&self, fact: Fact) -> String { /* "safe", "act", "load" */ }
fn question(&self, fact: Fact) -> Result<Question, ProtocolError> { /* the Noul, Choice or Score */ }
fn learned(&self, fact: Fact, judged: &Judged) -> Option<Value> { /* what an answer makes it */ }
}
Aside: it does not know what a rules engine is. Nothing here depends on one. The engine these projects use is
rete(in therustcratesrepository), whoseNetwork::nextreturnsNext::Ask(facts): exactly the listwantedtakes. Butreteknows nothing of Jev, and this crate knows nothing ofrete. A host with three hand-writtenifs can use it just the same. The two meet in the host, in about ten lines.
Aside: it does not send anything either.
wantedhands back aPrepared: the request body, exactly as it will go, and the most it can cost. The host sends it with whichever client it has (jev-workeron a Cloudflare Worker,jev-httpon a server). That is why a page can show the request while Jev is still thinking, and why the tests here need no network.
Try it.
cargo test -p jev-facts. The tests are a small domain of their own, a shell command with four facts. Readevery_wanted_fact_goes_in_one_request_and_a_shared_question_oncefirst; then makelearnedread the wrong type and watchan_answer_of_the_wrong_type_teaches_nothingname the question.
For the people who maintain it
learn is all or nothing: every fact with its value, or the first fact
that was not taught (Unlearned::NotAsked when the request had no such
question, Unlearned::WrongType when the answer is not the type asked). A
host never records half an answer.
prepare is the same step for questions that are not facts (lmjtfy's
second request asks questions a language model drafted): ids and questions
in, a Prepared out.
In this folder
| Path | What |
|---|---|
| src/lib.rs | Source, Judged, Part, Prepared, prepare, wanted, learn, Unlearned. |
| src/tests.rs | The command domain and six tests. |
| Cargo.toml | Depends on jev-protocol only. |
← Previous: Chapter 22¾: jev-ui/assets · Up: jevcrates · The end of the guide. Back to Chapter 16: jevcrates