next up previous contents
Next: What You Should Up: Another Interlude: Input/Output Previous: Input of Prolog

Defining Your Own Consult

For this, we need some additional information about the side-effecting predicate assert/1. Note that you should make use of this predicate as little as possible. If tempted to use it, think again.

The predicate assert/1 takes a legal Prolog clause as its argument. A call with a legal argument will always succeed with the side-effect of inserting the clause in the database ---usually, at the end of any clauses with the same principle functor and arity (there is a variant, asserta/1, which can be used to position a new clause for a predicate at the beginning).

Essentially, we redirect input to a named file, read a clause, assert it and recurse.

 
my_consult(File):-
			see(File),

my_read(X),

my_process(X),

seen.

my_process(X):-

\+(X=end_of_file),

my_assert(X),!,

my_read(Y),

my_process(Y).

my_process(X):-

\+(X=end_of_file),

my_read(Y),!,

my_process(Y).

my_process(end_of_file).

my_read(X):-

read(X),!.

my_read(X):-

my_read(X).

my_assert(X):-

assert(X).

There are some subtleties here. We have to consider various problems with, inevitably, different treatments.

The first problem is that of syntactically incorrect input. To handle this, we have defined a resatisfiable form of read/1. The predicate my_read/1 is designed so that, if read/1 fails, we just try again. Since read/1 has the side-effect of throwing away the offending input, we can have a go with another chunk of input. This mimics the behaviour of consult/1.

The second problem is to make sure that end_of_file is treated properly ---we do not want to insert it into our database nor do we want to force backtracking to take place back into my_read/1! The simplest solution is to realise that we only want to keep resatisfying my_read/1 if read/1 fails owing to a syntactic error. Once read/1 succeeds we would like to be committed. Hence we use case selection in my_process/1 making use of \+/1. This means that, on encountering end_of_file, we will use the third clause of my_process/1.

There is a third problem which this procedure can handle. There are syntactically correct Prolog terms which are not legal Prolog clauses. For example, a,b:-c. is a legal term but not a legal clause. The predicate my_assert/1 will fail and we will then try the second clause of my_process/1 which will pick up some more input and try to handle that. The cut ( !/0) is needed in the first and second clauses of my_process/1 because we are certain that if we have successfully `processed' a clause then we are committed from there on.

There is a fourth problem. If there is a query (or directive) in the file consulted such as ?- write(hello) then we do not want to assert this clause ---we want to issue some goal to the Prolog interpreter. This could be handled by two extra clauses for my_assert/1. One of these would be my_assert((?- X)):- !,call(X). Fixing this program to deal with this fourth difficulty can be left as an exercise for the reader (again).

The fifth problem is to write your own version of reconsult/1. This is a little trickier.

The sixth problem is not immediately obvious ---but remember that Prolog converts a grammer rule like s --> np,vp into something like s(S,S0):- np(S,S1), vp(S1,S0). Therefore, we ought to arrange to handle this.

In reality there is one further problem. It is possible to write one's own transformation rule to turn some legal Prolog clause into another one using term_expansion/2. This, however, can be hidden inside the call to the predicate that transforms grammar rules.


next up previous contents
Next: What You Should Up: Another Interlude: Input/Output Previous: Input of Prolog



Paul Brna
Mon May 24 20:14:48 BST 1999