Rete.Compiler.Negation (Rete v0.2.0)

Copy Markdown View Source

Turns a Rete.IR.CompoundNegation into something a Rete network can express.

Internal. A negation node watches one condition, and propagates its token while nothing matches. It cannot watch a conjunction. De Morgan's law does not rescue it here: the conjuncts share existentially quantified variables. With orders {1} and refunds {2}, "no x has both" is true, and the rewrite is false.

So the conjunction is lifted into a generated helper that inserts a marker fact, and the compound negation becomes an ordinary negation of that marker:

defrule clean({:customer, cid}, {:nand, [{:order, cid}, {:refund, cid}]})

# becomes, in effect
defrule clean__neg_1({:customer, cid}, {:order, cid}, {:refund, cid}) do
  {:"clean__neg_1", %{cid: cid}}
end
defrule clean({:customer, cid}, {:not, [{:"clean__neg_1", cid}]})

Three things make that correct.

The marker carries the bindings the conjunction joins on. Otherwise, one customer with both an order and a refund would suppress the rule for every customer. The helper repeats the preceding conditions, which is what binds those variables. The helper also fires first, through an :internal_salience set to the nesting depth. So extraction chains correctly, and the negating rule never observes an absence that had merely not been computed yet.

A helper's expressions are plain closures, because extraction runs at build time, long after macro expansion. See docs/design/network.md §6.

Summary

Types

A production and the helper productions extracted out of it.

Functions

Rewrites every compound negation in a production.

Whether a production was generated by extraction rather than written.

Types

extraction()

@type extraction() :: {Rete.IR.Production.t(), [Rete.IR.Production.t()]}

A production and the helper productions extracted out of it.

Functions

extract(production)

@spec extract(Rete.IR.Production.t()) :: extraction()

Rewrites every compound negation in a production.

Returns the rewritten production, and the helpers it generated, in the order they must be added to the network. Extraction runs depth-first, so a compound negation nested inside another appears before the helper that negates it.

A production with no compound negation comes back unchanged, with [].

generated?(production)

@spec generated?(Rete.IR.Production.t()) :: boolean()

Whether a production was generated by extraction rather than written.