8.4 Executing an ECLiPSe goal from Java and processing the result
The EclipseConnection interface
provides a single method rpc (Remote Predicate Call) for executing
goals in the ECLiPSe. This method takes as a parameter the goal to be
executed. How to construct this parameter is dealt with in Section
8.4.1. Section 8.4.2 explains
how to deal with the results returned by rpc. Finally, some more
details about the execution of the goal are given in Section
8.4.3.
8.4.1 Passing the goal parameter to rpc
There are main two variants of rpc which differ in the class of the
goal parameter.
The simplest way to use rpc is to pass it an instance of java.lang.String which should be the goal as it would be typed into
the ECLiPSe command line. Just like with the ECLiPSe command line,
the goal can be a conjunction of several subgoals. An example of this
is the use of rpc in the example program QuickTest.java. Also please note a full stop is not necessary.
The string-parameter rpc variant is somewhat inefficient and it
is also inflexible because creating goals dynamically, or goals
involving strings is tricky. It should really only be used for short
simple goals. A more flexible and efficient alternative is to invoke
rpc, passing it a parameter object which implements the CompoundTerm interface (discussed in Section
8.3.2). In this case the term becomes the
goal. Here is an example of using this version of rpc, taken
from
DataExample1.java.
CompoundTerm a_term = construct_term();
// Get Eclipse to write the term to stdout and flush
eclipse.rpc(
new CompoundTermImpl(",",
new CompoundTermImpl("write",
new Atom("output"), a_term),
new CompoundTermImpl("flush", new Atom("output"))
)
);
Using this variant is a bit more cumbersome (e.g. the creation of a
new CompoundTermImpl for the conjunction of goals in the above
example) but it would be useful for large goals constructed
dynamically. There are also a number of “convenience” rpc
methods, where instead of providing a CompoundTerm, you provide
the objects from which the term is made. See the API documentation for
more details of these variants.
The builtins yield/2 and remote_yield/1 should not be
executed anywhere within an rpc goal, as they will cause the
goal to return prematurely.
8.4.2 Retrieving the results of an rpc goal execution
The rpc method returns an object which implements CompoundTerm. This object is the Java representation of the goal term, with
the solution substitution applied to its variables.
The solution substitution can be deconstructed using the returned CompoundTerm's arg method. This method takes an integer (the argument
position) and returns an Object which is the Java representation of
the ECLiPSe argument at that position.
In the returned CompoundTerm instantiated variables are replaced
with their instantiations. Hence even if the variable was named in the
initial goal, its instantiation is identified in the returned goal by
its position rather than its name. Uninstantiated variables in
the returned CompoundTerm are represented using the Java null
token.
If a variable in the rpc goal becomes instantiated to an ECLiPSe
data type which does not have an equivalent EXDR type (such as breal), then
in the returned CompoundTerm it will appear as the Java null
token.
The following Java code is an example of how the returned CompoundTerm
can be deconstructed to extract the variable substitutions.
...
CompoundTerm result = eclipse.rpc("X = Q, Y is 2.1 + 7");
// The top-level functor of the goal term is ",".
// The first and second arguments of the goal term are the two subgoals
// and we can safely cast these as CompoundTerms.
CompoundTerm firstGoal = (CompoundTerm) result.arg(1);
CompoundTerm secondGoal = (CompoundTerm) result.arg(2);
// X is the first argument of the first goal.
Object firstGoalFirstArg = firstGoal.arg(1);
// Y is the first argument of the second goal.
Object secondGoalFirstArg = secondGoal.arg(1);
System.out.println("X = "+firstGoalFirstArg);
System.out.println("Y = "+secondGoalFirstArg);
...
The output will be:
X = null
Y = 9.1
Other ways an rpc invocation can terminate
Apart from succeeding and returning a CompoundTerm, rpc can throw
exceptions. If the goal fails, an instance of Fail is
thrown. So to test for failure you must catch this exception. An
instance of Throw is thrown if ECLiPSe itself throws an
error.
8.4.3 More details about rpc goal execution
Variables
As explained in Section 8.3.6, ECLiPSe
variables are always represented by the null token, when rpc is invoked with a CompoundTerm parameter. When used in an
rpc goal, each null token represents a different
variable. Using CompoundTerm you cannot represent a goal with
multiple occurrences of a single variable. So, for example the
following Java code will output q(b, b) for the first rpc
invocation and q(a, b) for the second.
...
eclipse.rpc("assert(q(a, b))");
eclipse.rpc("assert(q(b, b))");
System.out.println(eclipse.rpc("q(X, X)"));
System.out.println(eclipse.rpc(new CompoundTermImpl("q", null, null)));
...
Nondeterminism
The rpc feature does not support the handling of nondeterminism
in the execution of the ECLiPSe goal. If the goal succeeds, control
is returned to Java immediately after the first solution to the goal
is found in ECLiPSe. All choice-points thereafter are ignored. So,
for example, although the first ECLiPSe goal below would leave
choice-points, it would be equal in effect to the second.
...
result = eclipse.rpc("member(X, [1, 2, 3, 4, 5])");
...
result = eclipse.rpc("member(X, [1])");
This is not a practical problem. It merely implies that if you are
using nondeterminism to generate multiple solutions, you should
collect these on the ECLiPSe side using a meta-level built-in
predicate such as findall/3 and then return the result to Java.
Concurrent invocations of rpc
Note that the rpc method is synchronized. Therefore if,
while one Java thread is currently executing an rpc invocation,
a second Java thread tries to invoke the rpc method of the same
EclipseConnection, the second thread will block until the first
thread's rpc invocation returns.
Nested invocations of rpc
During the execution of the rpc method, control
is transferred to ECLiPSe. Due to the QueueListener feature
which is discussed in Section
8.5, control is sometimes temporarily
returned to Java before the ECLiPSe execution has finished. It is
possible for this Java code itself to invoke rpc, thus leading to nested rpc
invocations. Nested rpc invocations should not
cause any problems.