Working memory: everything a session knows, as one immutable value.
Internal. Not part of the public API, and documented rather than hidden because durability, checkpointing and advanced tooling will need to reach in here. Treat its functions as liable to change.
Five memories, plus one flag:
elements node_id => join_key => Bucket of Element right of a beta node
tokens node_id => join_key => Bucket of Token left of a beta node
accum node_id => join_key => group_key => [fact]
insertions node_id => token => [[fact]] truth maintenance
facts fact => count what it was toldThree properties are load bearing. See docs/design/engine.md §4.
- Arrival order. It decides the order tokens propagate, and so the order two
matches of one rule fire. A bucket that gave items back in another order would
reorder every
:activation_firedevent. - Removal collapses the level above. Every key above the leaf is a value, so an
entry pointing at an empty leaf leaks.
Rete.Engine.Nodesalso needs "no group" and "an empty group" to stay different answers. - Multisets, not sets. Inserting a fact twice and retracting once must leave it present. Two rules may each have concluded it.
root_seeded? is not a memory. It records that the beta root's empty token has been
planted, which must happen exactly once per session. See docs/design/engine.md §6.
iex> alias Rete.Memory
iex> {memory, :new} = Memory.add_fact(Memory.new(), {:order, 1})
iex> {memory, :duplicate} = Memory.add_fact(memory, {:order, 1})
iex> {memory, :remaining} = Memory.remove_fact(memory, {:order, 1})
iex> Memory.facts(memory)
[{:order, 1}]
Summary
Functions
Adds elements at a node under a join key.
Records a fact, returning {memory, :new | :duplicate}.
Records the facts one activation of a production inserted.
Adds tokens at a node under a join key.
Every element at a node, whatever its join key.
Every token at a node, whatever its join key.
Drops a collection group entirely.
The whole memory as plain data, every bucket rendered as a list in arrival order.
The elements stored at a node under a join key, in arrival order.
Every distinct fact the session holds.
The collection groups at a node under a join key, group_key => facts.
Records that the beta root's empty token has been propagated.
An empty memory.
Replaces the facts of one collection group.
Removes one occurrence of each given element, returning {memory, removed}.
Drops one occurrence of a fact, returning {memory, :gone | :remaining | :absent}.
Removes one occurrence of each given token, returning what was found.
Takes back one batch of facts a token's activation inserted.
The tokens stored at a node under a join key, in arrival order.
Types
@type node_id() :: term()
@type t() :: %Rete.Memory{ accum: %{ required(node_id()) => %{required(key()) => %{required(key()) => [term()]}} }, elements: %{ required(node_id()) => %{required(key()) => Rete.Memory.Bucket.t()} }, facts: %{required(term()) => pos_integer()}, insertions: %{ required(node_id()) => %{required(Rete.Token.t()) => [[term()]]} }, root_seeded?: boolean(), tokens: %{required(node_id()) => %{required(key()) => Rete.Memory.Bucket.t()}} }
Functions
@spec add_elements(t(), node_id(), key(), [Rete.Element.t()]) :: t()
Adds elements at a node under a join key.
Records a fact, returning {memory, :new | :duplicate}.
Only :new is propagated. A second insertion of an equal fact bumps its count so that
one retraction does not remove it. The matches it would make already exist.
@spec add_insertion(t(), node_id(), Rete.Token.t(), [term()]) :: t()
Records the facts one activation of a production inserted.
Stored as a list of lists. The same token can activate a production more than once over a session's life, and each activation owns its own batch.
@spec add_tokens(t(), node_id(), key(), [Rete.Token.t()]) :: t()
Adds tokens at a node under a join key.
@spec all_elements(t(), node_id()) :: [Rete.Element.t()]
Every element at a node, whatever its join key.
@spec all_tokens(t(), node_id()) :: [Rete.Token.t()]
Every token at a node, whatever its join key.
Drops a collection group entirely.
A group with no facts is not a group holding []. The first does not exist. The second
is an empty collection the rule can legitimately see. Only a grouping collection ever
drops to nothing.
The join key that held the last group goes with it, and the node with the last join key. Both are binding values, so leaving them behind would leak one entry per entity the session has seen.
@spec dump(t()) :: %{ elements: %{required(node_id()) => %{required(key()) => [Rete.Element.t()]}}, tokens: %{required(node_id()) => %{required(key()) => [Rete.Token.t()]}}, accum: %{ required(node_id()) => %{required(key()) => %{required(key()) => [term()]}} }, insertions: %{ required(node_id()) => %{required(Rete.Token.t()) => [[term()]]} }, facts: %{required(term()) => pos_integer()}, root_seeded?: boolean() }
The whole memory as plain data, every bucket rendered as a list in arrival order.
Reach for this instead of the struct. A Rete.Memory.Bucket holds a stack with
tombstones in it, and two memories that agree on every match can still disagree there.
This is the view that is meaningful to compare, assert on and write down.
@spec elements(t(), node_id(), key()) :: [Rete.Element.t()]
The elements stored at a node under a join key, in arrival order.
Every distinct fact the session holds.
The collection groups at a node under a join key, group_key => facts.
Records that the beta root's empty token has been propagated.
Rete.Engine.Nodes seeds only while this is false, so a session plants exactly one
root token however many times it is asked.
@spec new() :: t()
An empty memory.
Replaces the facts of one collection group.
@spec remove_elements(t(), node_id(), key(), [Rete.Element.t()]) :: {t(), [Rete.Element.t()]}
Removes one occurrence of each given element, returning {memory, removed}.
An element that was not there is left out of removed, so a caller can tell a real
retraction from a no-op. Propagating a retraction that never happened would corrupt the
counts downstream.
Drops one occurrence of a fact, returning {memory, :gone | :remaining | :absent}.
Only :gone — the last occurrence — is propagated.
@spec remove_tokens(t(), node_id(), key(), [Rete.Token.t()]) :: {t(), [Rete.Token.t()]}
Removes one occurrence of each given token, returning what was found.
@spec take_insertion(t(), node_id(), Rete.Token.t()) :: {t(), [term()]}
Takes back one batch of facts a token's activation inserted.
Returns {memory, facts}, or {memory, []} when the token never inserted anything.
That is a production retracted before it fired, or one whose body returned nothing.
@spec tokens(t(), node_id(), key()) :: [Rete.Token.t()]
The tokens stored at a node under a join key, in arrival order.