Fork me on GitHub

I’m running into some behavior that I don’t understand. I have a fact type of which I only ever expect 1 or 0 instances. If the session has 0 instances, I’d like to make a rule which inserts an instance with some reasonable default values. Right now I have this rule:

(defrule default-event-date
  "If there is no EventDate, create one far in the future."
  [:not [EventDate]]
   (r/map->EventDate {:date (time.coerce/to-date-time "30000")})))
This correctly does not fire if I start the session with an EventDate fact. If I don’t, however, run-rules never returns. It seems to just keep firing. I would expect it to fire once.


Ah, just found the mailing list thread about this. I guess that the fact gets retracted when the LHS stops being true, which causes it to get re-inserted, which causes the LHS to stop being true, which causes it to get retracted, etc. ad infinitum


The suggestion is to use insert-unconditional!, but that seems to never fire, even when there are no facts


@enn Hmm, insert-unconditional! should fire in example above if no EventDate facts are in the working memory when fire-rules is called. It might be worth restarting the REPL in case fact types got reloaded (and rules are looking at a different type). If things are still failing and can be narrowed down to a reproducible test I can take a look.


@ryanbrush I was mistaken—it does fire, it just doesn’t show up in explain-activations. Is that expected?


@enn So explain-activations uses the truth maintenance facility to figure out what is supporting the fact, which wouldn't be used in an unconditional insert. I think one could argue the fact that unconditional inserts aren't seen here is a bug, but there are tradeoffs there. Another use case for unconditional insert is for performance-critical edge cases where users don't want to pay the accounting cost of tracking truth and activation explanation...which we couldn't offer if we tracked truth maintenance/explain-activations for unconditional inserts.


@enn In short, I'd be open to adding unconditional activations to explain-activations, but I'd want to consider the tradeoffs and make sure our performance-critical use cases (some inserting more then 100,000 facts) have a path forward as well.


@enn As for the original problem of an infinite looping rule, this comes up enough that I think we need a better way to expose and explain it to the user. I've seen other engines limit the number of recursive activations to a few thousand and throw an exception with context to explain where the failure is coming from. Tempted to add that to Clara as well.


@ryanbrush: thanks for the explanation. That makes sense. I was just using explain-activations as a debugging tool, so now that I know it has that limitation, it’s not a problem that it’s not showing up.


@enn Fair enough. I mostly want to extinguish any sort of surprising behavior, even if it is for debugging purposes. I'll think about it some more but won't make any immediate changes here.