README.mdpreviewREADME.mdsource102 lines · 4.8 KB · raw

rete

A Rete rules engine that knows nothing about what it is deciding. It knows facts, tests on facts, priority, and a network in which rules that begin with the same tests share them. What the facts are is a domain, a type the host implements.

The name says what it is: a Rete network (Forgy, 1979). It is not called rules because a host has its own rules, and its own crate of them, on top of this one; lmjtfy's is packages/rules, which is the LMJTFY domain over rete.

What a host provides

A Domain names its facts, the values each can take, and what a rule may do:

ItemWhat it is
Fact, ValueWhat can be known, and what it can turn out to be.
EffectA step that costs a call and teaches one fact (Domain::teaches).
EndHow a rule stops the query outright.
NoteSomething that is so when a rule holds, and that decides no step.
asked_for(fact)Whether a source outside the network supplies the fact when asked, or an effect teaches it.
namesEach of the above has a name, which is the vocabulary of rules written as data and what a trace logs.

Rules are Rule { name, when: &[Test], then }, usually a const. Order is priority: of the rules that hold, the first with something left to do decides. Network::compile merges them.

The step that asks for everything at once

Some facts cost a round trip each. Network::next(&known) returns Next::Ask(facts) with every fact that any rule still alive is waiting on, so the host sends one request with N questions and records what comes back in Known. The other answers are Next::Do(effect), Next::End(end) and Next::Done. Nothing in the crate does I/O or is async: a host that must await between steps calls next in its own loop.

A host that can answer synchronously passes a Host, two callbacks (ask for the batch, perform for an effect), to Network::run. It returns the outcome, what is known, and a Trace. A source that does not supply a wanted fact stops the run as Outcome::Stuck rather than looping.

The trace

Every Run, and any step a host takes by hand (Network::explain), can say which rule fired and what it stood on. Trace::records() gives them as Records, which serialize to one JSON line each: the event (asked, fired, learned, done, ended, stuck), the rule's index and name, the names and values of the facts, and counts. Names, ids and counts only, never what a fact was about, so a host can log every state change.

{"step":2,"event":"fired","rule":2,"name":"look at it","then":"do review","facts":["touches code","lines changed"],"values":["yes","known"],"count":2}

Rules as data

load reads a JSON file and validates it against the domain's vocabulary. It fails, with every problem and the rule it is in, on an unknown fact, a value the fact cannot have, an effect, end or note the domain does not allow, a rule with no tests, a repeated or empty name, and any field it does not know. The rules it returns compile beside the ones written in code (LoadedRule::rule), after them, so code outranks data.

JSON, because every host already has a parser and its error says where it stopped. The array's order is priority. A review rule:

{ "rules": [
  { "name": "tests untouched",
    "when": [ { "fact": "touches code", "is": "yes" },
              { "fact": "touches tests", "is": "no" } ],
    "then": { "note": "ask for tests" } },
  { "name": "big change gets a look",
    "when": [ { "known": "lines changed" } ],
    "then": { "do": "review" } }
] }

A test is {"fact", "is"} or {"known"}; what a rule does is exactly one of {"do"}, {"end"} and {"note"}. A data rule cannot do I/O: the engine is pure, so it can read facts and say what to do, and the host decides whether to do it. A domain refuses by default (parse_effect, parse_end and parse_note return an error unless it opts in), so what a user may write is what the host wrote down. export writes code rules as the same data, to start a user from.

Use it

A project depends on it by path, through a git submodule of this repository (as lmjtfy does at third-party/rustcrates), and never copies it. Run the tests with mise run test from the repository root.

FileWhat it holds
src/domain.rsDomain, Test, Then, Rule and Known.
src/network.rsNetwork: compile, decide, next, held, used, explain.
src/trace.rsHost, Network::run, Trace and the Record a host logs.
src/load.rsRules as data: load, load_specs, export, and what can be wrong with a rule.
src/tests.rsThe engine and the loader on a small domain of their own, a change to be reviewed.

The picture of a network is the sibling crate rete-draw.