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, and de Morgan does not rescue it: the
conjuncts share existentially quantified variables, so 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, or one customer with both would suppress the rule for every customer. The
helper repeats the preceding conditions, which is what binds those variables. And
the helper 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. Helpers are extracted depth first, so a compound negation nested inside another appears before the helper that negates it.
A production with no compound negation is returned unchanged with [].
@spec generated?(Rete.IR.Production.t()) :: boolean()
Whether a production was generated by extraction rather than written.