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
@type extraction() :: {Rete.IR.Production.t(), [Rete.IR.Production.t()]}
A production and the helper productions extracted out of it.
Functions
@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 [].
@spec generated?(Rete.IR.Production.t()) :: boolean()
Whether a production was generated by extraction rather than written.