This library enables the communication with an R process started as a shared library. Version 1, was the result of the efforts of two research groups that have worked in parallel. The syntactic emphasis on a minimalistic interface. Versions between 1.4 and 2.0, also work done by others particularly in interfacing to web applications. See credits for more details.
In the doc/ directory of the distribution there is user's guide, a published paper
and html documentation from PlDoc. There is large number of examples in examples/for_real.pl
.
By default when the library is loaded an R object is started which will serve the
R commands. If current_prolog_flag(real_start,false)
succeeds, the R object is not loaded and the user
needs to issue r_start/0 to do that.
A single predicate (<-/2,<-/1) channels the bulk of the interactions between Prolog and R. In addition to using R as a shared library, real uses the c-interfaces of SWI/Yap and R to pass objects in both directions. The usual mode of operation is to load Prolog values on to R variables and then call R functions on these values. The return value of the called function can be either placed on R variable or passed back to Prolog. It has been tested extensively on current SWI and YAP on Linux machines but it should also compile and work on MS operating systems and Macs.
Since v1.1 Real supports threads for web services and v1.3 it supports running an R server in any thread, not just the main thread. The library now has the concept of a designated R server thread. By default, there is no designated server thread, and the evaluation/execution of R expressions/commands is done in the calling thread. This should be done in a single threaded way. A designated server thread can come into existence in one of three ways:
r(r_thread_loop_stop)
or <- r_thread_loop_stop
in any thread.r_call_as_server(G)
. While G is running, the thread that
it is running in becomes the designated server thread, and G should call r_serve/0
periodically to answer any R requests that accumulate.
While there is a designated server thread, a call to r/1, r/2, (<-)/1 or (<-)/2 in any
thread results in the request being posted to the server thread and the current thread
blocking until a reply is received. As of July 2016 SWI-Prolog also has an alternative
pack (pack(rserve_client)
) which works with Rserve and Swish.The main modes for utilising the interface are
<- +Rexpr <- +Rvar
Print Rvar or evaluate expression Rexpr in R
+Rvar <- +PLdata +Rexpr <- +PLdata -PLvar <- +Rvar -PLvar <- +Rexpr +Rexpr1 <- +Rexpr2
Pass Prolog data to R, pass R data to Prolog or assign an R expression to an assignable R expression.
There is a raft of examples packed in a sinlge file that test the library.
?- [pack(real/examples/for_real)]. ?- for_real. ?- edit( pack(real/examples/for_real) ).
There are syntactic conventions in R that make unparsable prolog code. Notably function and variable names are allowed to contain dots, square brackets are used to access parts of vectors and arrays and functions are allowed empty argument tuples. We have introduced relevant syntax which allows for easy transition between prolog and R. Prolog constructs are converted by the library as follows:
..
within atoms -> .
(ex. as..integer(c(1,2,3)) -> as.integer(c(1,2,3))
)^[]
after atoms -> []
(ex. a^[2] -> a[2]
)(.)
at the end of atoms that are known R functions -> ()
(ex. dev..off(.) -> dev.off()
)[]
-> c()
(which equal to R's NULL value)f(x)
:- (..)) -> f(x)
(...)foo()
is valid syntax: <- dev..off()
works now (with no need for dev..off(.)
)m[1] <- 4
works now (with no need for m^[...])prolog_flag( allow_dot_in_atom, true )
.R vectors are mapped to prolog lists and matrices are mapped to nested lists. The convention works the other way around too.
There are two ways to pass prolog data to R. The more efficient one is by using
Rvar <- PLdata
Where Pldata is one of the basic data types (number,boolean) a list or a c/n term. This transfers via C data between R and Prolog. In what follows atomic PLval data are simply considered as singleton lists. Flat Pldata lists are translated to R vectors and lists of one level of nesting to R matrices (which are 2 dimensional arrays in R parlance). The type of values of the vector or matrice is taken to be the type of the first data element of the Pldata according to the following :
Booleans are represented in prolog as true/false atoms. Currently arrays of aribtrary dimensions are not supported in the low-level interface. Note that in R a scalar is just a one element vector. When passing non-scalars the interface will assume the type of the object is that of the first scalar until it encounters something different. Real will currently re-start and repopulate partial integers for floats as illustrated below:
r <- [1,2,3]. % pass 1,2,3 to an R vector r R <- r. % pass contents of R vector r to Prolog variable R R = [1, 2, 3]. i <- [1,2,3.1]. % r is now a vector of floats, rather than integers I <- i. I = [1.0, 2.0, 3.1].
However, not all possible "corrections" are currently supported. For instance,
?- c <- [a,b,c,1]. ERROR: real:set_r_variable/2: Type error: `boolean' expected, found `a'
In the data passing mode we map Prolog atoms to R strings-
?- x <- [abc,def]. true. ?- <- x. [1] "abc" "def" true. ?- X <- x. X = [abc, def].
In addition, Prolog data can be passed through the expression mechanism. That is, data appearing in an arbitrary R expression will be parsed and be part of the long string that will be passed from Prolog to R for evaluation. This is only advisable for short data structures. For instance,
tut_4a :- state <- c(+"tas", +"sa", +"qld", +"nsw", +"nsw"), <- state. tut_4b :- state <- c(+tas, +sa, +qld, +nsw, +nsw), <- state.
Through this interface it is more convenient to be explicit about R chars by Prolog prepending atoms or codes with + as in the above example.
The Prolog atoms '$NaN' and '' are passed to NA values in R. '$NaN' is the bidirectional value, '' is only understood in the Prolog -> R direction as it is useful for passing missing values from CSV read matrices.
nan_ex :- x <- [c(1,2,''),c(3,4,'$NaN')], X <- x, write( x(X) ), nl. ?- nan_ex. x( [[1, 2, '$NaN'], [3, 4, '$NaN']] )
Use r_citation/2 to access publication information about the interface. Although the original name was R..eal, when citating please use Real as the name for this library.
The library listens to
?- debug(real). ?- nodebug(real).
Predicate <<-/2 is a shorthand that ensures that the R variable on the left is fresh/new at the time of call, and <<-/1 blanks R variable out (r_remove/1).
?- e <- numeric(.). yes ?- e^[3] <- 17. yes ?- e[3] <- 17. yes ?- Z <- e. Z = ['$NaN','$NaN',17.0] ?- e^[10] <- 12. yes ?- Z <- e. Z = ['$NaN','$NaN',17.0,'$NaN','$NaN','$NaN','$NaN','$NaN','$NaN',12.0] rtest :- y <- rnorm(50), % get 50 random samples from normal distribution <- y, % print the values via R x <- rnorm(y), % get an equal number of normal samples <- x11(width=5,height=3.5), % create a plotting window <- plot(x,y) % plot the two samples r_wait, % wait for user to hit Enter % <- dev..off(.). % old syntax, still supported <- dev..off(). % close the plotting window. foo() now acceptable in supported Prologs tut6 :- d <- outer(0:9, 0:9), fr <- table(outer(d, d, "-")), <- plot(as..numeric(names(fr)), fr, type="h", xlab="Determinant", ylab="Frequency"). tut4b :- state <- [tas,sa,qld,nsw,nsw,nt,wa], statef <- factor(state), incmeans <- tapply( c(60, 49, 40, 61, 64, 60, 59), statef, mean ), <- incmeans. logical :- t <- [1,2,3,4,5,1], s <- t==1, <- s, S <- s, write( s(S) ), nl.
The following predicates are exported, but not or incorrectly documented.