Working with R and JS Types

library(QuickJSR)

Mappings and Conversions Between R and JS Types

QuickJSR uses the respective C APIs of R and QuickJS in order to pass values between the two. This allows for increased efficiency in passing and returning values (as no serialisation or de-serialisation is required) and also allows for greater flexibility in working with R closures, functions, and environments in JS code.

QuickJSR aims to broadly follow the conventions of jsonlite in terms of how R types are converted to JS types and vice-versa.

Primitive & Scalar Types

The following table outlines the basic mappings of primitive types between R and JS types:

R Type JS Type
NULL null
logical boolean
integer number
double number
character string
date date
POSIXct date
factor string

Note that the handling of Date/POSIXct types differs from jsonlite, where they are converted to strings. In QuickJSR, they are treated directly as Date objects in JS.

Container Types

The following table outlines the basic mappings of container types between R and JS types:

R Type JS Type
named list object
unnamed list array
vector array
array array
matrix 2D number array
data.frame array of objects

Examples of the matrix and data.frame conversions are shown below:

m <- matrix(1:6, nrow = 2)
cat(to_json(m))
#> [[1,3,5],[2,4,6]]
df <- data.frame(a = 1:3, b = c("x", "y", "z"))
cat(to_json(df))
#> [{"a":1,"b":"x"},{"a":2,"b":"y"},{"a":3,"b":"z"}]

Note that the to_json() function operates by converting R objects to their JS equivalents, and then calling JSON.stringify() on the result. This allows you to explore how different types are being converted to JS.

Functions and Closures

Functions and closures can be passed between R and JS code. In JS, functions are represented as Function objects, and can be called directly from JS code.

ctx <- JSContext$new()
ctx$source(code = "function callRFunction(f, x, y) { return f(x, y); }")

ctx$call("callRFunction", function(x, y) x + y, 1, 2)
#> [1] 3
ctx$call("callRFunction", function(x, y) paste0(x, ",", y), "a", "b")
#> [1] "a,b"

Working with R Environments

R environments are represented in JS as a custom class: REnv. The REnv class simply wraps the pointer to the R environment, and provides methods for getting and setting values - this means that there is only a ‘cost’ for conversion when values or accessed or updated.

Environment values can be accessed using either env.value or env["value"] syntax:

ctx$source(code = 'function env_test(env) { return env.a + env["b"]; }')
env <- new.env()
env$a <- 1
env$b <- 2
ctx$call("env_test", env)
#> [1] 3

Values in the environment can also be updated from JS code:

ctx$source(code = "function env_update(env) { env.a = 10; env.b = 20; }")
ctx$call("env_update", env)
#> NULL
env$a
#> [1] 10
env$b
#> [1] 20

Accessing Package Namespaces & Functions

QuickJSR automatically adds a global object R to each context, which can be used to access the namespaces of installed packages - and subsequently extract and use functions and objects from them.

qjs_eval('R.package("base").getwd()')
#> [1] "/tmp/RtmphsKERK/Rbuild77b4f5f84f7/QuickJSR/vignettes"