Previous: Have combinator, Up: Interfaces to External Code


2.7.16.3 Interaction combinator

A further combinator allows virtual code applications to interact directly with any interactive console application using the expect library. The mechanism is similar to that of interactive applications documented in the Output From Interactive Applications, but attempts to be more convenient. Instead of being designed as an interactive application, any virtual code application may use this combinator to spawn a shell and interact with it in order to compute some desired result.

The advantage of this combinator over the library combinator is that it requires no modification of the virtual machine to support new applications. It can also interact with applications that may reside on remote servers, that are implemented languages other than C, or whose source code is unavailable. For example, the GNU R statistical package provides an interactive command to evaluate multivariate normal distribution functions with an arbitrary covariance matrix, but the corresponding function is not provided by the Rmath C library (or any other free library, to the author's knowledge) because it is implemented in interpreted code. This combinator makes it callable by an avram virtual code application nevertheless. The disadvantage compared to the library combinator is that there is more overhead in spawning a process than simply making a call to a built in function, and the programming interface is more complicated.

The combinator takes the form

T35
[[interact]] f = ((nil,nil),(((nil,nil),nil),((nil,f),nil)))

where f is the virtual code for a function that follows the same protocol described in Output From Interactive Applications, except that it does not allow file output as described in Mixed Modes of Interaction. The argument x is ignored when the expression (interact f) x is evaluated, similarly to the way the argument is ignored in an expression like (constant k) x. The result returned is a transcript of the dialogue that took place between f and the externally spawned shell, represented as a list of lists of strings for line oriented interaction, or a list of characters alternating with lists of strings in the case of character oriented interaction.

The following example demonstrates a trivial use of the interact combinator to spawn an ftp client, do an ls command, and then terminate the session.

     
     eof = <(nil,(nil,(((nil,nil),nil),(nil,nil))))>
     
     demo =
     
     interact conditional(
        conditional(identity,constant false,constant true),
        constant(0,<'ftp'>,<'ftp> '>),
        conditional(
           conditional(left,constant false,constant true),
           constant(1,<'ls',''>,<'','ftp> '>),
           conditional(
              compose(compare,couple(left,constant 1)),
              constant(2,<'bye',''>,<eof>),
              constant nil)))

Some liberties are taken with silly syntax in this example, in the way of using angle brackets to denote lists, and numbers to represent states.

Deadlock would be possible at any point if either party did not follow this protocol, but for this example it is not an issue. If an expression of the form demo x were to be evaluated, then regardless of the value of x, the value of the result would be as shown below.

     <
        <'ftp'>,
        <'ftp> '>,
        <'ls',''>,
        <'ls','Not connected.','ftp> '>,
        <'bye',''>,
        <'bye',''>>

That is, it would be a list of lists of strings, alternating between the output of the interactor and the output of the ftp client. If the spawned application had been something non-trivial such as a computer algebra system or a command line web search utility, then it is easy to see how functions using this combinator can leverage off a wealth of available resources.