JPL 2.x Getting Started


This section provides a tutorial introduction to JPL through its JPL 2.x Java API overview, the interface most programmers are likely to use.  The source code described here can be found in the examples directory of the JPL distribution.  Feel free to consult the source files and run the demonstration program as you read this section.

Verifying the installation

To confirm that JPL and SWI-Prolog are basically able to work together, open a console window and go into this directory:
jpl/examples/
read the README.txt file, and run the various examples which it describes.

Each Java example will run as an application (i.e. each has a main() method), exercises SWI-Prolog, and writes something to System.out or System.err.

If you see some plausible output, with no serious error messages, then all may be well.

Creating a Prolog database in a text file

To experiment with JPL, we'll first create a Prolog database in a text file.  We will eventually load this database into the Prolog engine through the JPL 2.x Java API overview.

Type the following in a text editor and save the result in a file called test.pl

child_of(joe, ralf).
child_of(mary, joe).
child_of(steve, joe).

descendent_of(X, Y) :-
    child_of(X, Y).
descendent_of(X, Y) :-
    child_of(Z, Y),
    descendent_of(X, Z).

You may wish to load this database into an interactive Prolog session to experiment with the predicates in this database before experimenting with JPL.

Initializing The Prolog engine

Although the jpl.JPL class provides a number of methods for initializing the Prolog engine from within Java, their use is not usually necessary: Prolog will be automatically initialised with default parameters at the first attempt to use it.

Consulting the Prolog database from its text file

In an ordinary interactive Prolog session, we'd load the above Prolog database using the Prolog consult/1 predicate, a built-in predicate in standard Prolog.  Note, however, that as a Prolog predicate, "calling" consult/1 is just an example of making a Prolog query, and this is how we perform it with JPL.

First we construct an instance of jpl.Query, whose name is consult and whose arguments (just one) comprise the atom 'test.pl':

Query q1 =
    new Query(
        "consult",
        new Term[] {new Atom("test.pl")}
    );
Then we call the query() method of this Query object, which returns a Boolean value indicating its success:
System.out.println( "consult " + (q1.query() ? "succeeded" : "failed"));
At this point, this process may seem a bit long-winded; however, you should soon see that the classes are sufficiently general that they provide a robust and powerful interface into the Prolog engine.  There is also considerable scope for writing "convenience" classes and methods, but in this introduction we deliberately employ the general, primitive facilities of the JPL 2.x Java API overview.

Querying the Database

Using the same technique, we can query the Prolog database about inferences it can make.  To ask whether the Prolog query child_of(joe,ralf)is true, given the above Prolog database, for example, we write:
Query q2 =
    new Query(
        "child_of",
        new Term[] {new Atom("joe"),new Atom("ralf")}
    );

System.out.println(
    "child_of(joe,ralf) is " +
    ( q2.query() ? "provable" : "not provable" )
);

To take an example that requires a bit more work on the part of the Prolog engine, on the other hand, we can ask whether descendent_of(steve,ralf) is true:
Query q3 =
    new Query(
        "descendent_of",
        new Term[] {new Atom("steve"),new Atom("ralf")}
    );

System.out.println(
    "descendent_of(joe,ralf) is " +
    ( q3.query() ? "provable" : "not provable" )
);

Querying with Variables

A ground query is relatively straightforward; it is essentially either provable or not, and there is typically no point in backtracking.  Once we use variables, however, things get a bit more complicated.

Using the jpl.Variable class, we can construct a non ground query; and using other methods of Query we can obtain a solution in the form of a java.util.Hashtable.  If the Query has one or more solutions, then its Query.oneSolution() method returns a Hashtable representing the first solution, otherwise it returns null:

Variable X = new Variable();

Query q4 =
    new Query(
        "descendent_of",
        new Term[] {X,new Atom("ralf")}
    );

java.util.Hashtable solution;

solution = q4.oneSolution();

System.out.println( "first solution of descendent_of(X, ralf)");
System.out.println( "X = " + solution.get(X));

The HashTable contains bindings in the form of Terms, each of which is indexed by its corresponding Variable in the Query.

Finding all solutions

The previous query finds only the first solution.  Often, however, one wants all solutions, or at least more than just the first.  The Query class also provides the allSolutions() method, which returns an array of zero or more Hashtables, each of which represents a given solution.

In this example we reuse the query q4, which was reset to its initial state by the call of oneSolution(), and instead call allSolutions(), which returns an array of solutions:

java.util.Hashtable[] solutions = q4.allSolutions();

for ( int i=0 ; i<solutions.length ; i++ ) {
    System.out.println( "X = " + solutions[i].get(X));
}

Equivalently, one can obtain each solution by exploiting the Enumeration interface, which the Query class implements.  In this example, we iteratively call hasMoreSolutions() and nextSolution() to exhaustion:
System.out.println( "each solution of descendent_of(X, ralf)");
while ( q4.hasMoreSolutions() ){
    solution = q4.nextSolution();
    System.out.println( "X = " + solution.get(X));
}
In this final example, we reuse the previous variable X with a new variable Y in a new query q5:
Variable Y = new Variable();

Query q5 =
    new Query(
        "descendent_of",
        new Term[] {X,Y}
    );

while ( q5.hasMoreSolutions() ){
    solution = q5.nextSolution();
    System.out.println( "X = " + solution.get(X) + ", Y = " + solution.get(Y));
}

The hasMoreSolutions method of the Query class returns a boolean, indicating whether there are any solutions "left" in the query.  If the answer to this is 'yes', then the solution can be obtained in the form of a Hashtable by the nextSolution method.
Note.  By calling hasMoreSolutions you are actually making the query to the Prolog engine; the "answer" to the query is cached in the Query class instance and returned from nextSolution.

Where to Go From Here

This section provides a brief tutorial on getting started with the High-Level Interface.  You should read the JPL 2.x Java API overview section for more information about using these interfaces.  Feel free to consult the JPL 3.x Java API reference section for detailed information about particular classes.

December 2003 (revised)