11.1 Database manipulation

Prolog has four database manipulation commands: assert, retract, asserta, and assertz. Let's see how these are used. Suppose we start with an empty database. So if we give the command:

listing.

we simply get a yes; the listing (of course) is empty.

Suppose we now give this command:

assert(happy(mia)).

It succeeds (assert commands always succeed). But what is important is not that it succeeds, but the side-effect it has on the database. If we now give the command:

listing.

we get the listing:

happy(mia).

That is, the database is no longer empty: it now contains the fact we asserted.

Suppose we then made four more assert commands:

assert(happy(vincent)).
yes
 
assert(happy(marcellus)).
yes
 
assert(happy(butch)).
yes
 
assert(happy(vincent)).
yes

Suppose we then ask for a listing:

listing.
 
happy(mia).
happy(vincent).
happy(marcellus).
happy(butch).
happy(vincent).
yes

All the facts we asserted are now in the knowledge base. Note that happy(vincent) is in the knowledge base twice. As we asserted it twice, this seems sensible.

So far, we have only asserted facts into the database, but we can assert new rules as well. Suppose we want to assert the rule that everyone who is happy is naive. That is, suppose we want to assert that:

naive(X) :- happy(X).

We can do this as follows:

assert( (naive(X) :- happy(X)) ).        

Note the syntax of this command: the rule we are asserting is enclosed in a pair of brackets. If we now ask for a listing we get:

happy(mia).
happy(vincent).
happy(marcellus).
happy(butch).
happy(vincent).
 
naive(A) :-
    happy(A).

Now that we know how to assert new information into the database, we need to know how to remove things form the database when we no longer need them. There is an inverse predicate to assert, namely retract. For example, if we go straight on and give the command:

retract(happy(marcellus)).

and then list the database we get:

happy(mia).
happy(vincent).
happy(butch).
happy(vincent).
 
naive(A) :-
    happy(A).

That is, the fact happy(marcellus) has been removed. Suppose we go on further, and say

retract(happy(vincent)).

and then ask for a listing. We get:

happy(mia).
happy(butch).
happy(vincent).
 
naive(A) :-
    happy(A).

Note that the first occurrence of happy(vincent) (and only the first occurrence) was removed.

To remove all of our assertions we can use a variable:

retract(happy(X)).
 
X = mia ;
 
X = butch ;
 
X = vincent ;
 
no

A listing reveals that the database is now empty:

listing.
yes

If we want more control over where the asserted material is placed, there are two variants of assert, namely:

  1. assertz. Places asserted material at the end of the database.

  2. asserta. Places asserted material at the beginning of the database.

For example, suppose we start with an empty database, and then we give the following command:

assert( p(b) ), assertz( p(c) ), asserta( p(a) ).                

Then a listing reveals that we now have the following database:

p(a).
p(b).
p(c).
yes

Database manipulation is a useful technique. It is especially useful for storing the results to computations, so that if we need to ask the same question in future, we don't need to redo the work: we just look up the asserted fact. This technique is called `memoization', or `caching'.

Here's a simple example. We create an addition table for adding digits by using database manipulation.

additiontable(A) :-
    member(B,A),
    member(C,A),
    D is B+C,
    assert(sum(B,C,D)),
    fail.

(Here member/2 is the standard membership predicate which tests for membership in a list.)

What does this program do? It takes a list of numbers A, uses member to select two numbers B and C of this list, and then adds B and C together calling the result D. Now for the important bit. It then asserts the fact that it has discovered (namely that D is the sum of A and B), and then fails. Why do we want it to fail? Because we want to force backtracking! Because it has failed, Prolog will backtrack to member(C,A) and choose a new value for C, add this new C to B two create a new D, and then assert this new fact. it will then fail again. This repeated failure will force Prolog to find all values for member(B,A) and member(C,A), and add together and assert all possible combinations.

For example, when we give Prolog the command

additiontable([0,1,2,3,4,5,6,7,8,9])

It will come back and say No. But it's not this response that interests us, its the side-effect on the database that's important. If we now ask for a listing we see that the database now contains

sum(0,0,0).
sum(0,1,1).
sum(0,2,2).
sum(0,3,3).
sum(0,4,4).
sum(0,5,5).
sum(0,6,6).
sum(0,7,7).
sum(0,8,8).
sum(0,9,9).
sum(1,0,1).
sum(1,1,2).
sum(1,2,3).
sum(1,3,4).
sum(1,4,5).
sum(1,5,6).
sum(1,6,7).
sum(1,7,8).
sum(1,8,9).
sum(1,9,10).
    .
    .
    .
    .
    .

Question: how do we remove all these new facts when we no longer want them? After all, if we simply give the command

retract(sum(X,Y,Z)).

Prolog is going to go through all 100 facts and ask us whether we want to remove them! But there's a much simpler way. Use the command

retract(sum(_,_,_)),fail.

Again, the purpose of the fail is to force backtracking. Prolog removes the first fact about sum in the database, and then fails. So it backtracks and removes the next fact about sum. So it backtracks again, removes the third, and so on. Eventually (after it has removed all 100 items) it will fail completely, and say No. But we're not interested in what Prolog says, we're interested in what it does. All we care about is that the database now contains no facts about sum.

To conclude our discussion of database manipulation, a word of warning. Although it can be a useful technique, database manipulation can lead to dirty, hard to understand, code. If you use it heavily in a program with lots of backtracking, understanding what is going on can be a nightmare. It is a non-declarative, non logical, feature of Prolog that should be used cautiously.


Patrick Blackburn, Johan Bos and Kristina Striegnitz
Version 1.2.5 (20030212)