Assertz after existing rule

I’d like to assertz after an existing rule (which might not be the last in the database). Is it possible?
I see that Prolog has assertz/2 that provides a reference, but it’s only used for clause/3 and erase/1.

It seems like it would be very useful, given that Prolog cares about rule ordering.

There is no such thing. The clause reference thingy is part of old practices but never made it to the ISO standard. Still around in quite a few systems though and it won’t disappear from SWI-Prolog.

Yes, order is important. This mostly applies to the predicates that form the logic of the application though and which may want to avoid non-terminating recursion or use cuts to avoid lots of negated conditions (as in one clause doing X < 0 and the other X >= 0). Most dynamic predicates are simple facts, so no recursion and no cuts that make ordering necessary.

I have been thinking several times about adding an assert-before predicate, but it is not really easy, would make code less portable and so far I never found a sufficiently convincing case to justify such a beast. So, maybe you can elaborate a bit more on the context such that we can give some advice on how to deal with this or finally decide it is worthwhile to seriously consider adding such a facility.

When I run into problems that I don’t have a solid understanding of and have not found a viable solution, I keep the idea in the back of my mind and then when reading try and reason how that might apply.

In this case the idea of applying Prolog Many Worlds to this problem might apply. Instead of trying to shoehorn a new predicate into a existing set of predicates, the new predicate would be added to a new Prolog World (currently think module) and then depending upon which Prolog World is referenced, a selection of predicate(s) is made. Make sense?

I say think module but I don’t believe a module is accurate enough due to lack of ways of coordinating communication between modules.

Perhaps the future of Terminus DB might be of benefit.

Broadly, I have a database of transformation rules (O(10,000)), that form part of a larger inference engine. The database needs to be queried and updated interactively. The transformation rules have the form:

transform(State, Context, State_, RuleID_) :-    
     more_conditions(State, Context),    % implementation expanded in-place
     new_state(State, Context, State_),  % implementation expanded in-place
     RuleID = 1234.                      % Just for tracking what rule matched
  • State and Context are tree-like data-structures:
    State = tree(node(...more data...), [ ...tree children... ] )

  • To allow SWI to index the head, we expand State (and sometimes Context) in the head: (e.g.: )

   transform(tree(node(tok{x:a, y:_, z:c}), ChildrenTreeList),  _, State_, RuleID_) :- 
          ... more conditional tests on ChildrenTreeList ....  
          ... State_ = ...
  • The database is generated incrementally, and interactively updated, through a mix of manual and automatic processes. When generating new rules, it’s helpful to examine existing (conflicting) rules and decide whether the new rule should have higher or lower precedence than others, and insert the new rule before or after them in the database (and delete rules the new rule subsumes), and iterate.

More or less, the ruleset database is queried against each token of a document - it should be real-time or better. Currently it is - thanks for indexing! The bulk of the database doesn’t have to be built in real-time. However:

  • rules are generated with a human-in-the-loop process. So we should be able to test and insert new rules (before or after existing rules) more or less in real-time.

  • being able to both insert and test against the new database will also speed up database generation which helps my day go faster,

A solution is to dispose and re-assert the entire database for each incremental change. I could do it, but I would prefer a better way.

If we are talking 10k clauses and human interactivity this is probably just fine as collecting, modifying and re-asserting 10k clauses is probably just a few ms (depending on the complexity of the clauses).

I agree it is not very elegant. A faster solution could be to use the State argument to setup some sort of tree, so you get something like below.

transform(state(1, ...), ...) :- transform_state1(....).
transform(state(2, ...), ...) :- transform_state2(....).

Even if there is nothing in State to base this tree on, you could create pools, as in

transform(...) :- transform1(...).
transform(...) :- transform2(...).

Now you only have to reorder one of the transformN() pools. Possibly there is something about your rules that makes sure you can define a certain set of pools such that inside the pool asserta/1 or assertz/1 does the job.

Just a few ideas … I guess one of these is workable. Indeed still not very nice. Not sure whether it justifies the tricky insert-in-the-middle. I propose a pragmatic approach, but keep this discussion open in case the pragmatic approach gets too ugly/slow/…

1 Like