Rete.Network.Node (Rete v0.1.0)

Copy Markdown View Source

The node descriptions the engine runs.

Internal. A node is data, not behaviour. It holds the join keys and captured expression functions the engine needs, and nothing else. Rete.Engine.Nodes implements activation against these structs.

No node carries its children. They are forward edges in Rete.Compiler.BetaGraph. A node is shared between rules exactly when it is equal, and equality must not depend on how many rules have hung children off it yet.

:id is nil until the node is in a graph. A node is described first and inserted second, so nil means "a description, not yet a node". Every node reachable from a built Rete.Network has an id. See docs/design/network.md §3.

Summary

Functions

Puts an id on a node.

The value that decides whether two conditions collapse onto one node.

How a terminal names itself in a listener event.

Whether a node is a terminal: a production or a query.

Types

Functions

put_id(node, id)

@spec put_id(t(), non_neg_integer()) :: t()

Puts an id on a node.

sharing_key(n)

@spec sharing_key(t()) :: term()

The value that decides whether two conditions collapse onto one node.

Built from expression codes and join keys. Never from captured functions, which are never equal, and never from :id, which is assigned after the sharing decision. An equal key is necessary but not sufficient: Rete.Compiler.BetaGraph also requires an identical parent set.

A terminal keys on its production's identity, so two rules with an identical left hand side still get their own terminal and fire independently.

iex> alias Rete.Network.Node
iex> Node.sharing_key(%Node.Negation{type: :order, alpha_code: :a1, join_bind: [:cid]})
{:negation, :order, :a1, [:cid]}

source(map)

@spec source(Rete.Network.Node.Production.t() | Rete.Network.Node.Query.t()) :: %{
  node: term(),
  rule: {module(), atom()}
}

How a terminal names itself in a listener event.

Rete.Listener.handle_event/2 cannot reach the network, so a node id alone would be unresolvable. {module, name} is the identity everything else uses. A map rather than a wider tuple, so a field can be added without changing the shape every listener matches on.

iex> node = %Rete.Network.Node.Production{id: 12, module: MyRules, name: :flag}
iex> Rete.Network.Node.source(node)
%{node: 12, rule: {MyRules, :flag}}

terminal?(arg1)

@spec terminal?(t()) :: boolean()

Whether a node is a terminal: a production or a query.

iex> Rete.Network.Node.terminal?(%Rete.Network.Node.Query{})
true
iex> Rete.Network.Node.terminal?(%Rete.Network.Node.HashJoin{})
false