InterProlog:
A simple yet powerful
Java/Prolog interface

Miguel Calejo, Servisoft, Portugal

Servisoft Development (http://dev.servisoft.pt) is a portuguese software developer and consultant company, part of a small private group (http://www.servisoft.pt/pessoas/mcstuff/profile/servisoft.htm). In addition to contract work for corporate customers, it also participates in applied research and experimental development projects with academia.

As a result of its participation in the PROLOPPE project (http://www-ia.di.fct.unl.pt/~lmp/documents/proloppe.html) a Java/Prolog interface was developed: InterProlog, whose preliminary code and documentation are available under the terms of the GNU Public Library License from http://dev.servisoft.pt/interprolog/. It is an ongoing project geared towards supporting future applications, so all contributions are welcome at this stage.

Java and Prolog

As logic programmers we need a second language; the world simply isn't totally declarative. Unless you're willing to ignore that, or to stick to a heavy weight Prolog environment providing all sorts of operational OS/GUI/DB primitives, including the proverbial kitchen sink or perhaps even your own pet Prolog extension, it's obvious we need a second language to make projects useful and complete. This is due to both technical AND economical reasons: logic programming foundations still shake occasionally for the good, and we should leave the door open for innovation (e.g., better alternative Prolog engines) to propel results forward; on the other hand, maintenance costs are a factor advising careful choice of tools. In other words, focus your Prolog engine work on declarative issues, tackle other things elsewhere.

We see Java as a dream language for this scenario. Multi-platform, cleanly object-oriented, dynamic, heavily adopted. InterProlog is an attempt at capitalizing on this opportunity, taking full advantage, among others, of Java's Reflection and Serialization APIs: Reflection allows arbitrary messages to be sent to Java from Prolog, without any specific need for Java programming; Serialization provides a uniform and open mechanism to exchange data between both, by having Java sending and receiving from Prolog serialized byte streams representing Java object state

. Following is a short overview of InterProlog's main features; the curious reader is invited to browse the online documentation at http://dev.servisoft.pt/interprolog/.

Overview

InterProlog is a Java application (JDK 1.1.x based, running under Windows 95/NT or Solaris) encapsulating access to a faceless Prolog, currently XSB Prolog (http://www.cs.sunysb.edu/~sbprolog/xsb-page.html), running in a background process with redirected I/O and connected to a couple of TCP/IP sockets. When you launch InterProlog you get a standard top level listener window, plus the ability to graphically browse terms in outline form. Due to space limitations we'll refer all environmental issues to http://dev.servisoft.pt/interprolog/, and focus just on the basic capabilities.

Any standard Prolog can be supported with a minimal effort, as long as it is able to do 8-bit clean communication with sockets. Any OS supporting Java 1.1 should be easily accommodated too.

The Prolog side

A single primitive gives the Prolog programmer nearly unlimited power, courtesy of Java's reflective capabilities: predicate javaMessage(Target,Result,Message) synchrounously sends Message to object Target, obtaining a Result object (which may be null for void methods). This predicate can be used to send any message to any public method in Target's class or superclasses, without the need for any specific coding on the Java side. javaMessage is available in simpler or richer varieties, from 2 to 6 arguments depending on whether you wish to handle Java-side exceptions, obtain new state of object arguments, etc.

For example, "hello world" using the Java console can be done simply with

javaMessage(Target,Message): ?- javaMessage('java.lang.System'-out,println(string('Hello world!'))).

Specifying objects, such as in targets and message arguments, is a complex matter handled mostly on the Prolog side. This simple example specifies class variable "out" in class System, as well as a String object. The next section presents the generic mechanism for object synthesis/analysis in InterProlog.

In addition to javaMessage, if your application has a Swing (http://java.sun.com/products/jfc/) based GUI a number of predicates are available, see http://dev.servisoft.pt/interprolog/html/Introduction.htm

Understanding and specifying objects in Prolog

Before looking at InterProlog from the perspective of the Java programmer, let's look at a global issue first, data conversion.

Objects data structures may be very complex in general, but Java 1.1 incorporated a powerful notion already tested in older OO systems: object serialization. In a word, a systematic and uniform way to have any object persist/read its internal state into/from a serialized byte representation, appropriate for example for file storage or network transmission. Older systems lacking Java's reflective capabilities needed specific code in each class to implement the serialization mechanism.

The first good news is: the Serialization API allows a Java programmer to efficiently "save" the whole state of basically any object into a byte stream, with a single method call, by a clever use of reflection already done by the Java implementers; and the better news is: that byte stream format is standard and public, and InterProlog knows it, in the form of a Definite Clause Grammar. This grammar is both able to parse objects sent from Java, and to synthesize new objects from Prolog term specifications, through their serialized byte stream representation.

Based on this principle, a template-based facility was developed with the Prolog programmer in mind. If the Java programmer develops a class for an application, and later he wants the Prolog programmer to manipulate it, all he has to do is to provide a pair of class instances, enclosed in a ObjectExamplePair object, to a PrologEngine (see next section), using its method teachMoreObjects(); then a Prolog predicate is generated automatically, allowing a template-based understanding/specification of Java objects by the Prolog programmer, using logical variables to easily access or set only those object variables the Java programmer saw as relevant (by providing different values in the instance pair).

For example, let's assume that for a particular application we have a MyEmployee class, with instance variables name and age, and a constructor with two arguments. In addition to the class code itself only the following Java code is needed:

ObjectExamplePair[] myExamples = {

new ObjectExamplePair( new MyEmployee("Miguel",37), new MyEmployee("Gabriela",34) ) };

engine. teachMoreObjects(myExamples);

teachMoreObjects() takes an array of example pairs and passes it to Prolog, where a InterProlog predicate analyses them, resulting in the assertion of an ipObjectSpec fact. From then onwards the Prolog programmer can use MyEmployee objects, so later he may pass it to javaMessage as targets or arguments:

?- , ipObjectSpec('MyEmployee',EmployeeObject,[Age,Name],ExamplePairData),

ExamplePairData contains representations of the two examples, and may serve as a documentation aid to the Prolog programmer.

InterProlog comes with predefined ipObjectSpec facts for all Java basic types (non-objects such as int, etc.), as well as several classes. It is also possible to refer objects created on the Java side, but whose internal state has not passed to the Prolog side.

The Java side

A single class, PrologEngine, gives the Java programmer access to Prolog. The current implementation supports XSB Prolog from SUNY at Stony Brook, USA. Each background Prolog process is encapsulated in a PrologEngine instance. Methods are available to ask Prolog to evaluate goals and get an objectified result, or simply to send text to its stdin and collect its stdout, through the use of a simple listener mechanism based on Java background threads.

Here's a full Java example, with no GUI:

import pt.servisoft.interprolog.*;
public class BackendTester{
public static void main(String args[]) {
PrologEngine engine = new PrologEngine();
// Our goal is simply "X=1234"; in order to get X's //binding back to Java,we must build an object at the //Prolog side somehow //representing it.
// We'll use an Integer;
String goal = "X=1234, ipObjectSpec('java.lang.Integer',ObjectSpec,[X],_)";
Object[] bindings = new Object[1]; // place for 1 binding
String variableNames = "[ObjectSpec]"; // Prolog variable with the object being bound
if (engine.deterministicGoal(goal, variableNames, bindings)){
System.out.println("We got "+bindings.length+" variable bindings");
for(int b=0; b<bindings.length; b++)
System.out.println("Binding "+b+":"+bindings[b].toString());
}else System.out.println("Goal failed");
}
}


The program will print "Binding 0:1234". Notice that multiple bindings can be returned, that's why the variableNames String contains a (string representation of) a Prolog list, and bindings is an Object array.

Conclusion

We've only skimmed at InterProlog, and you may find it worth your while to browse the documentation and slides in http://dev.servisoft.pt/interprolog/. Java Reflection, Serialization, communication, and GUI capabilities have combined with Prolog's intrinsic strengths to provide a very exciting tool, still young and requiring some patience, a work in progress adjusting to customer projects that are now starting. Please send any suggestions to interprolog@servisoft.pt, so we may improve it more efficiently. We're also interested in assisting anyone willing to port it to other Prologs, which is basically simple to do.

Acknowledgments are due to the PROLOPPE (Praxis/3/3.1/TIT/24/94) and REAP (Fundacao Luso-Americana para o Desenvolvimento) projects, namely to Luis Moniz Pereira, as well as to the XSB group, in particular to David S. Warren and Terry Swift.

Email: mc@servisoft.pt 30-Oct-1998