Assertion Creep

I’m using: SWI-Prolog version 8.3.8

I use a concurrent_forall to search for solutions. When one is found, I use assertz(result(S) to store it. After the concurrent piece, I use a sequential forall to write each result to disc. When I’m done, I retract it all for cleanup. But, it doesn’t clean up. If I run the program again, the output contains the original solutions and a duplicate. The next time there are two duplicates, etc. I have to exit Prolog and restart in order to get only the output I want. Deleting the output file before re-running does not help.

parallel(Puzzle, Scope):-
    retractall(result/1),
    rules(Puzzle, [Rule1|Rules]),
    concurrent_forall(generate(Puzzle, Rule1, Sequence), test(Puzzle, Rules, Sequence, Scope)),
    forall(result(S), wrt_nl(S, Puzzle, tw)),
    retractall(result/1).


test(_, [], Sequence, _):-
%    write(Sequence).
    assertz(result(Sequence)).

test(_, [Scope|_], Sequence, Scope):-
    assertz(result(Sequence)).

test(Puzzle, [Rule|Rules], Sequence, Scope):-
    plb(Puzzle, Lock, Bond),
    check_rule(Puzzle, Rule, Sequence, Lock, Bond, Rule_Flag),
    Rule_Flag = true_flag,
    test(Puzzle, Rules, Sequence, Scope).


test(Puzzle, [Rule|_], Sequence, _):-
    plb(Puzzle, Lock, Bond),
    check_rule(Puzzle, Rule, Sequence, Lock, Bond, Rule_Flag),
    dif(Rule_Flag, true_flag).

You are calling retractall/1 incorrectly. See the docs. You are doing:

retractall(result/1).

but you should have done:

retractall(result(_)).

This should be retractall(result(_)).
See the documentation for retractall/1: All facts or clauses in the database for which the head unifies with Head are removed.
result/1 doesn’t unify with result(foo).

1 Like

Thanks much for both replies. It’s quite clear now that I see it. I think the use of the word all in the name, combined with the _ in the argument looked like a “double inclusive” to me. And yes, I had looked at the documentation of retractall/1. But I hadn’t looked up the precise meaning of “the Head unifies with the head”.

Yes. So this is how you can use this in practice, for the next person who might be wondering:

?- forall(member(X-Y, [a-1, b-2, a-3, b-4]), assertz(result(X, Y))).
true.

?- listing(result/2).
:- dynamic result/2.

result(a, 1).
result(b, 2).
result(a, 3).
result(b, 4).

true.

?- retractall(result(a, _)).
true.

?- listing(result/2).
:- dynamic result/2.

result(b, 2).
result(b, 4).

true.

This is almost visible in the user comments under the docs for retractall/1, but I agree that the examples there don’t make this immediately obvious.

There is another catch: retractall/1 does implicitly declare its argument as a dynamic predicate, as you see from the example above. However, the SWI-Prolog compiler will sometimes complain if the dynamic predicate is used in the source code and you don’t explicitly declare it with dynamic.

With this file, called dynamic.pl:

run(List, Y) :-
    retractall(foo(_)),
    forall(member(X, List), assertz(foo(X))),
    bar(Y).

bar(X) :-
    foo(X).

It works as intended but running make/0 issues a warning:

?- [dynamic].
true. % Everything is OK

?- make.
Warning: The predicates below are not defined. If these are defined
Warning: at runtime using assert/1, use :- dynamic Name/Arity.
Warning: 
Warning: foo/1, which is referenced by
Warning: 	dynamic.pl:7:4: 1-st clause of bar/1
true.

?- run([a,b,c], X).
X = a ;
X = b ;
X = c.

Ideally retractall(p/1) should raise a type error, but it is in the end legal as it is legal to have a predicate (/)/2, e.g.

?- assertz(p/1).
?- p/1.
true,
?- retractall(p/1).
?- p/1.
false,

We have seen similar confusion before. Would it be an idea to reserve the (/)/2 such that we can raise errors when a head term is expected and a / term is given? Note that reserving is nothing new: all ISO predicates are reserved in the sense that the user cannot create predicates with the same name.

edit Thanks to @jamesnvc auto-linking, we see that (/)/2 is actually a predicate used by library(yall). That changes the discussion a bit. We would for example like to raise an error on predicate_property(p/1, P), but this would disable reasoning about library yall. In itself this is not a big deal, but it is a hard to explain exception.

Alternatively, a pair of examples and a warning in the docs? I can volunteer to write a couple of sentences and two small examples as higher up in the thread.

But then, I wouldn’t know what to say about the warning that pops up with make/0. Or is that irrelevant.

The question on how to document this keeps popping up. I guess we all agree that examples are the way to go. The question is how though. We to do with a number of related predicates: dynamic/1, assert/1 and some queries. Do we show these as a program? Interactive might be more helpful, but than the dynamic/1 directive is a strange beast (it isn’t always needed, but adding it to the example shows good style). If we have this example, we better link it from all involved predicates.

One option could be to have a whole lot of examples written as markdown and stored somewhere. The manual would parse the code in all examples and link or include it it from all involved predicates. For example, we could host that as a wiki on Discourse and link it automatically from the online manual. That doesn’t sound overly complicated to realize. Would it work to get a whole lot of community examples and keep everything manageable?

In theory, library(check) could do type and mode checks on arguments that are already specified in the source code. Might be a good idea. Type checking 0.0 :slight_smile:

1 Like

It looks like I set off more of a discussion than I had anticipated. There is a nice example in the manual for retract/1. A similar example for retractall/1 would certainly solve the problem for me. I still would not understand, however, why there is both a retract and a retractall. Intuitively, it seems that simply using retract with an anonymous variable like “retract(result())" would serve the same purpose as "retractall(result())”. I’m sure I’m missing something here, but the manual doesn’t make it clear to me.

It seems I misspoke regarding unification. It is certainly true that result/1 does not unify with result(foo). It is also true that result/1 does unify with result1, since they are the same thing. The problem is that the nature of head (with lower case h) is not obvious. I’m sure that if I read the manual from cover to cover and knew all the proper definitions by heart there would be no problem, but I’m afraid I’m just not up to that.
FYI, I took one course in logic programming (under Leon Sterling) in 1989, and haven’t had a chance to use it until now.
Thanks again for the help from all of you.

Note there are two instances (not instantiations) in my previous reply where and underscore was not printed between two parentheses. The argument in both cases for result, should read result(_), not result(). I’m not sure why it came out funny. If it still is wrong after I enter this reply, think of it as result(underscore). Sorry for any confusion.

Oops, one more typo. I meant to say result/1 unifies with result/1 since they are the same thing. I’ll try to do a better job of proof reading my posts.

Are you using email or are you using the website at https://swi-prolog.discourse.group?

If you are using the website to read and post, you can edit your posts. There should be a “pencil” icon in the bottom of your own posts; click it and you can edit it to get rid of typos.

The site uses some markdown. This means that italics are done using _underscores on both sides_ of whatever you are writing. If you want to type in verbatim, you can put backticks on both sides:

This is some text and `this will be printed verbatim`.

This way your underscores won’t get swallowed by the markdown processor.

Sorry for the long post, don’t have time for a short one.

It is difficult to cater to everyone’s tastes I guess. I would assume that some really just go to www.swi-prolog.org and read the docs, as it is supposed to be the ultimate source. Others would just google things and use stackoverflow or the search functionality of this discourse.

My personal preference would be to have mini-tutorials that are prominently linked from the docs. They can live in their own space on the swi-prolog.org site but are linked from all relevant predicate docs. You have already done that for many topics, one prominent example is “How to create a web service easily” (very informative and useful, btw!).

The point here is that you have full control over the content and there is no risk that a change of the business model of Discourse for example would force you to somehow move whatever has collected over the years. But maybe I am just paranoid.

On the topic of content for the howtos, I would say that there is a wealth of examples here as well as on stackoverflow. However, the stackoverflow stuff is sometimes quite biased, depending on who wrote it and at what stage of their Prolog journey they did it. I am also not absolutely sure about the legal aspects of just copying stuff from stackoverflow. At the moment at least I consider my own (somewhat dated) contributions on stackoverflow as an utter waste of my time.

Furthermore, some ideas from what I have seen over the years. “The C Programming Language” book is a masterpiece IMHO because through well-chosen problems and solutions it contains a nice collection of “this is how you do that thing in C”.

The C++ books written by good old Bjarne Stroustrup are interesting because he shows not only how to use a library feature but also shows how it is implemented. “The Craft of Prolog” by Richard O’Keefe is similar in spirit, but it is a dead tree book, it is not available online, and its age might be scary to the younger audience. There is a wealth of such examples in the SWI-Prolog library as well, they are just not annotated and linked as prominently as they could be.

“The Python Tutorial” is IMHO a killer feature of the Python language because it is gives the programmer a thing to read through once and just start writing useful programs, and also serves as a starting place for exploring the standard library.

Just some thoughts. I think I have expressed the same views before. I have tried more than once to somehow salvage the effort I put into writing stackoverflow answers by organizing it into a “Tutorial” but I always end up thinking “I just don’t know enough to write something truly useful”.

The main difference between retract/1 and retractall/1 is that the argument of retract/1 has to match the whole rule and not only the head. On the other hand you have abolish/1 but you can’t use it to selectively retract from the database, it just completely wipes out a predicate using its predicate indicator.

All these things make more sense if you read once through the Glossary of Terms. What is a predicate, a rule, a fact, a predicate indicator, the rule’s head and body and so on.

:slight_smile: Thanks for the answer. I largely agree. Seems we mostly agree it would be a good idea to have examples as stand alone entities and have them linked (probably automatically). The point is a bit how to get these examples and where to store them. Well, as long as they are markdown I don’t care too much where they are stored. We could use Discourse. It has an API, so we can easily get the stuff out of there and store it elsewhere. Even if there is no instance we can download the SQL database and I’m sure it isn’t too hard to get the examples out of that. We could also opt for a git repository.

Ideally examples come from real professionals. You seem to agree most people are not born as professional Prolog programmer though :slight_smile: I think we need something more community driven.

ISO abolish/1 is mistake. It can only abolish dynamic predicates for which we already have retract/1 and retractall/1 and then the predicate looses its (dynamic) property. Originally, abolish/1 in most systems was intended to get rid of and (including static) code and be able to replace it at runtime. That makes more sense, but of course not all compilation models are compatible with this idea. ISO should simply have dropped abolish/1.

Note that retractall/1 using a is_most_general_term/1 Head is detected and results in a quick wipe of all clauses without actually matching the heads.

Not to derail further, but would this be a good use case for the new(ish) isolated transactions? E.g. snapshot/1:

snapshot/1 allows a thread to examine a frozen state of the dynamic predicates and/or make isolated modifications without affecting other threads and without making permanent changes to the database.

It seems that this is what OP really wants: if they edit the original tests to assert new types of results (say, result2(_) for simplicity) then they would have to make sure that all the retractions are updated to match, whereas if they do it in a snapshot they can throw away the intermediate state after they’re done without worrying about other ways the database could have been side-effected. Yes?

1 Like

I fear no :slight_smile: The OP uses assert/retract to communicate between threads while snapshot/1 isolates threads :frowning: (well, actually :slight_smile: as that is why we have snapshots). In general, snapshot/1 is a good idea to run algorithms that use the dynamic database as it both provides isolation (and thus ensures the code is thread safe) and ensures the database is unchanged at the end. This is a quite elegant alternative to the more conventional thread_local/1 and setup_call_cleanup/3 approach.

1 Like