Douglas Bates
February 24, 2015
In [1]:
    
using DataArrays,DataFrames,RCall
    
The file defining the C API for R is R.h in the directory whose name is ENV["R_INCLUDE_DIR"].  In my case this is
In [2]:
    
joinpath(ENV["R_INCLUDE_DIR"],"R.h")
    
    Out[2]:
R.h causes inclusion of several other header files from that directory, the most important of which is Rinternals.h.
A typical function declaration in Rinternals.h is
Rboolean Rf_isVector(SEXP);
We can call this function directly from Julia using ccall.  We define a Julia function isRVector to do this.
In [3]:
    
isRVector(s::SEXPREC) = ccall((:Rf_isVector,libR),Bool,(Ptr{Void},),s.p)
    
    Out[3]:
In [4]:
    
"library(robustbase)" |> rcopy
    
    Out[4]:
In [5]:
    
m1 = reval("m1 <- lmrob(Y ~ ., coleman, setting = 'KS2011')")
    
    Out[5]:
In [6]:
    
isRVector(m1)  # true because m1 is a list
    
    Out[6]:
In [7]:
    
isRVector(m1[1]) # true because the coefficients are a numeric vector
    
    Out[7]:
In [8]:
    
typeof(m1[18])  # the "call" component is a LANGSXP (function call)
    
    Out[8]:
In [9]:
    
isRVector(m1[18]) # a LANGSXP does not behave like a vector
    
    Out[9]:
We can simplify many of the ccall expressions by creating a convert method to produce a Ptr{Void} from the Julia SEXP type
Base.convert(::Type{Ptr{Void}},s::SEXP) = s.p
In fact, because all these predicate functions have the same signature, we can (and do) define corresponding Julia functions through metaprogramming.
## predicates applied to an SEXP
for sym in (:isArray,:isComplex,:isEnvironment,:isExpression,
            :isFactor,:isFrame,:isFree,:isFunction,:isInteger,
            :isLanguage,:isList,:isLogical,:isSymbol,:isMatrix,
            :isNewList,:isNull,:isNumeric,:isNumber,:isObject,
            :isOrdered,:isPairList,:isPrimitive,:isReal,
            :isS4,:isString,:isTs,:isUnordered,:isUnsorted,
            :isUserBinop,:isValidString,:isValidStringF,
            :isVector,:isVectorAtomic,:isVectorizable,
            :isVectorList)
    @eval $sym(s::SEXP) =  ccall(($(string("Rf_",sym)),libR),Bool(Ptr{Void},),s)
end
Many of these functions; isComplex, isExpression, isList, isLogical, isNewList,isSymbol, isReal and isS4, are redundant because the Julia SEXP type is a templated type with the code built-in.  At present only a few of these functions are exported.
Note that the meaning of Array is slightly different for R and for Julia.  In Julia a vector is an Array.  In R a vector is an Array only if it has a dim attribute.
In [10]:
    
RCall.isArray(m1[1])  # coefficients are a vector but not an R array
    
    Out[10]:
In [11]:
    
RCall.isArray(m1[22]) # the model matrix is an array
    
    Out[11]:
There are several methods for the sexp generic function that create a Julia SEXP from other types of Julia objects.
For example, an R symbol is retrieved (installing it in the symbol table if necessary) via a C function whose signature is
SEXP Rf_install(const char *);
We define an sexp method for the Symbol type in Julia as
function sexp(s::Symbol)
    sexp(ccall((:Rf_install,libR),Ptr{Void},(Ptr{Uint8},),
               string(s)))
end
(If you know about R's garbage collection and are wondering why the result is not protected, objects in the symbol table are never garbage collected.)
In [12]:
    
sexp(:foo)
    
    Out[12]:
In [13]:
    
rprint(ans)