Custom Conversion

Custom Conversion

RCall supports an API for implicitly converting between R and Julia objects by means of rcopy and RObject.

To illustrate the idea, we consider the following Julia type

type Foo
    x::Float64
    y::String
end
foo = Foo(1.0, "hello")

Julia to R direction

The function RCall.sexp has to be overwritten to allow Julia to R conversion. sexp function takes a julia object and returns an SEXP object (pointer to [Sxp]).

import RCall.sexp

function sexp(f::Foo)
    r = protect(sexp(Dict(:x => f.x, :y => f.y)))
    setclass!(r, sexp("Bar"))
    unprotect(1)
    r
end

roo = RObject(foo)
nothing # hide

Remark: RCall.protect and RCall.unprotect should be used to protect SEXP from being garbage collected.

R to Julia direction

The function rcopy and rcopytype are responsible for conversions of this direction. First we define an explicit converter for VecSxp (SEXP for list)

import RCall.rcopy

function rcopy(::Type{Foo}, s::Ptr{VecSxp})
    Foo(rcopy(Float64, s[:x]), rcopy(String, s[:y]))
end
rcopy (generic function with 97 methods)

The convert function will dispatch the corresponding rcopy function when it is found.

rcopy(Foo, roo)
convert(Foo, roo) # calls `rcopy`
Foo(roo)
nothing # hide

To allow the automatic conversion via rcopy(roo), the R class Bar has to be registered.

import RCall: RClass, rcopytype

rcopytype(::Type{RClass{:Bar}}, s::Ptr{VecSxp}) = Foo
boo = rcopy(roo)
nothing # hide

Using @rput and @rget is seamless

boo.x = 2.0
@rput boo
R"""
boo["x"]
"""
R"""
boo["x"] = 3.0
"""
@rget boo
boo.x

Nested conversion

l = R"list(boo = boo, roo = $roo)"
rcopy(l)