"I have a mind like a steel... uh... thingy." Patrick Logan's weblog.

Search This Blog

Wednesday, December 07, 2011

Calling Prolog from Java with Prolog Cafe

A couple two, three more details from yesterday's post. The first is how to establish the package name for a Prolog source file being compiled to Java:

Simply put the following at the top of the Prolog source:

:- package 'com.example.foo'.

Now, calling predicates from Java: each predicate and arity is compiled from Prolog to its own Java class. For example the following Prolog source:

main :-
 queens(8, Qs), write(Qs), nl, fail.

queens(N,Qs) :-

queens(UnplacedQs,SafeQs,Qs) :-

%% ...and so on.

If you omit the package name then the package defaults to "user" and the compiler creates a "user" directory for the Java source. Otherwise the Java files will be placed in a nested source tree corresponding to the package name you've defined.

The section of code above defines three predicates:

  • main/0 is compiled to a class named PRED_main_0 (i.e. a class for a predicate with no arguments, "arity zero")
  • queens/2 is compiled to a class named PRED_queens_2 (i.e. a class for the two-argument predicate, "arity two")
  • queens/3 is compiled to a class named PRED_queens_3 (i.e. a class for the three-argument predicate, "arity three")

These predicates can be called from Java using the Prolog Cafe API. The predicate main/0 will display the results to standard output using the write/1 and nl/0 predicates which write a given term and write a newline, respectively. Running that predicate was illustrated in yesterday's post.

If you want to find the values of the logic variables given in a goal, then the Java would look something like:

IntegerTerm eight = new IntegerTerm(8);
VariableTerm solution = new VariableTerm();
PRED_queens_2 queens = new PRED_queens_2(eight, solution, Failure.FAILURE);
BlockingPrologControl prolog = new BlockingPrologControl();
StringBuffer buf = new StringBuffer();
for (boolean r = prolog.call(); r; r = prolog.redo()) {
    buf.append("A solution: " + solution + "\n");
queens_solutions = buf.toString();

This Java code creates an instance of the queens/2 predicate corresponding to the following Prolog: queens(8, Qs), fail.

Constructors for a predicate instance require a "continuation" argument. Lacking no other interesting continuation, the default choice is Failure.FAILURE, a built-in predicate telling the search to backtrack for further solutions.

The IntegerTerm eight is a constant, telling the application to try to place eight queens. And the VariableTerm is a logic variable telling the application to unify found solutions with this variable.

BlockingPrologControl is going to conduct the search to satisfy its given predicate. The call method tries to find a solution, and returns true if it does. Subsequent calls to redo try to find further solutions, finally returning false when no more solutions exist.

The loop iterates once per solution. With each iteration the value of the logic variable, solution, is unified with the current solution. This code simply builds a string of all of the solutions, and I happened to use just one logic variable in the goal.

If you're not (yet) a Prolog programmer, note that a clause can have any number of variables, and the search will attempt to satisfy each of them so that the whole solution is true. Given the following definition of append/3, in which the third term is logically equated to the first term appended to the second term:

append([X|Y],Z,[X|W]) :- append(Y,Z,W).

The following goal will attempt to find values for each of the variables such that the two appends are satisfied:

append([N], [Y, Z], [X, 2, 3]), append([3], [2, 1], [3, 2, N]).

There is one solution:

N = 1,
Y = 2,
Z = 3,
X = 1

See that the variables N and X are unified to each other, and unified to the number 1 as well.

Tuesday, December 06, 2011

Prolog in Java

There are a number of implementations of Prolog in Java. Most of them are interpreters, and a number of those implement a complete Prolog system. Most implementations are a bit dated.

Wanting to get a bit more speed but remain in the JVM, I took a look at Prolog Cafe, which compiles Prolog to Java source. Even better, the source is as recent as 2009.

Then I looked around to see who might be using Prolog Cafe, and how recently. Even better news: The Gerrit code review system uses Prolog Cafe for rule checking. The team has been actively supporting a fork off the last original release of 1.2.5.

I just began trying Prolog Cafe (the new fork) this morning. The most significant difference so far is the Java package namespace. Below are a few notes illustrating the system, based off the original documentation, using the new namespace, etc.

  1. Install SWI Prolog for bootstrapping.
  2. cd to the prolog-cafe source directory.
  3. make

The package namespace has changed from
to: com.googlecode.prolog_cafe.*.

Prolog Cafe has an interpreter. Try it:

java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.lang.PrologMain com.googlecode.prolog_cafe.builtin:cafeteria
Prolog Cafe 1.2.5 (mantis)
Copyright(C) 1997-2009 M.Banbara and N.Tamura
| ?- [queens].
{consulting /home/patrick/dev/prolog-cafe-ex/queens.pl ...}
{/home/patrick/dev/prolog-cafe-ex/queens.pl consulted 350 msec}
| ?- main.

Compile Prolog to Java:

java -cp $PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.compiler.Compiler ../queens.pl

Compile the resulting Java:

javac -d . -cp $PLCAFEDIR/plcafe.jar user/*.java

Run the compiled code:

java -cp .:$PLCAFEDIR/plcafe.jar com.googlecode.prolog_cafe.lang.PrologMain com.googlecode.prolog_cafe.builtin:cafeteria

Prolog Cafe 1.2.5 (mantis)
Copyright(C) 1997-2009 M.Banbara and N.Tamura
| ?- main.

Blog Archive

About Me

Portland, Oregon, United States
I'm usually writing from my favorite location on the planet, the pacific northwest of the u.s. I write for myself only and unless otherwise specified my posts here should not be taken as representing an official position of my employer. Contact me at my gee mail account, username patrickdlogan.