Multiple Call-in Combiner

This example shows the use of multiple Prolog interactors from within the same thread. An interpreter allows spawning interactors in a piggyback fashion. This cares for the situation that the solving of a goal of an interactor performs a call-out which in turn performs a call-in again. Besides piggybacking interactors we also allow the forking of an interpreter inside the same thread and spawning from there new interactors.

In the following example we use two predicates p/1 and q/1 that define simple one column tables of data. We then use one interpreter respective interactor to run along the first table and another interpreter respective interactor to run along the second table. The first interpreter is created as usual from the knowledge base, but the second interpreter is created from an existing interpreter:

        Interpreter inter = know.iterable();
Interpreter inter2 = inter.iterable();

Creating an interpreter from a knowledge base should be done only once. Under the hood this will create an interpreter object and a controller object. Forking a new interpreter from an existing interpreter can then be done multiple times. Each forked interpreter will share the initial controller object. The controller object carries shared data such as the input/output streams and the thread local predicates.

We construct a goal for the first table and a goal for the second table. The creation of interactors from the two interpreters then works as follows:

        CallIn callin = inter.iterator(pGoal);
CallIn callin2 = inter2.iterator(qGoal);

The application programmer can freely switch between differently forked interpreters inside the same thread. The controller object can be used to interrupt any of the interpreters that are currently used in the thread. We will use this switching capability to implement a simple combiner that alternately displays a row from the first table and a row from the second table. This is a stream programming pattern that has many applications.

We use a Boolean flag to decide which of the two tables should be displayed. We have to loop through both tables as long as both tables still have rows:

        boolean flip = true;
while (callin.hasNext() && callin2.hasNext()) {
if (flip) {
callin.next();
wr.write("p(");
wr.write(Term.toString(0, inter, pVars[0]));
wr.write(").\n");
wr.flush();
} else {
callin2.next();
wr.write("q(");
wr.write(Term.toString(0, inter2, qVars[0]));
wr.write(").\n");
wr.flush();
}
flip = !flip;
}

The code then also contains the handling of each of the single tables. When we run the complete code we get the following result:

p(a).
q(1).
p(b).
q(2).
p(c).
p(d).

The various iterable() and iterator() methods have been introduced in release 1.0.10 of Jeke-jeke Prolog together with the new controller object. A new feature is also the static method currentInterpreter() which does a lookup of the current interpreter for the current thread. These methods do not yet implement or yield the corresponding interfaces from Java since they throw exceptions. This might improve in the future.

Interactors were mainly introduced for the Prolog Java proxy classes demonstrated in the subsequent section. The handler of a Prolog Java proxy class uses an interactor to execute a corresponding predicate or evaluable function. Since this execution uses methods from the nextClose() family the Prolog Java proxy class mechanism cannot yet be used to leave a trace of forward chaining or to fill a constraint store.

Kommentare