How to best undo PL_assert in embedded Prolog

We are trying to embed SWI-prolog in a larger program, where we would like to use it to compute a function by encoding the problem in Prolog, making a query, and extracting the result. This function may be called any number of times, so the changes to the Prolog state should not be persistent (except for some static prelude, maybe).

What we want to do to encode our problem is basically load some Prolog prelude from a Prolog file (which defines some functors and rules, fixed for all calls), and programmatically add facts about dynamic predicates using PL_assert. These facts should not be conserved between calls, and need to be reverted somehow after the query result has been decoded.

What is the best way to achieve this?

We are not sure what mechanism we can rely on. We initially thought that foreign frames would do, but they don’t seem to remove facts added with PL_assert when discarded. We also tried using modules: running our query in a different module each time, using PL_assert with the given module. But that also does not seem to work as we expect. Could it be that the prelude that we load defines things that are in the top-level context? The documentation of PL_assert says that the facts are added to the database under the given module, but what if the predicate is not itself defined in the same module?

1 Like

Foreign frames only deal with term_t handles. PL_assert() simply calls asserta/1 or assertz/1 with some flags that define how the predicate is created if it does not exists yet.

The typical scenario is that you dump the data in one or a fairly small number of well known predicates that are empty when no “run” is in progress. You do the asserts, do the computation and call retractall/1 on the predicates that hold the data.
Instead of doing many retractall/1 calls from C(++)/…, you typically define a predicate such as cleanup that does all the cleanup and you call that from foreign code.

But, that is the simple scenario. There are other scenarios where the set of predicates is not know, the code must be used from multiple threads, etc … Then you get to thread_local/1 predicates, temporary modules or transactions …

2 Likes

OK thanks, retractall/1 seems to be enough for now.

FYI

You may not need this for your use case but it is nice to know in case you need it.

assertz/2 used with erase/1

clause/3 to convert a head and body to a reference or vice-a-versa