Executing an ECLiPSe goal from Java and processing the result

## 8.4 Executing an ECLiPSe goal from Java and processing the result

- Passing the goal parameter to rpc
- Note: yield/2, remote_yield/1 and rpc
- Retrieving the results of an rpc goal execution
- More details about rpc goal execution

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.

### Note: yield/2, remote_yield/1 and rpc

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.