Skip to contents

The Introduction to luajr vignette shows you how to get started with calling Lua code from R. This vignette shows you how to integrate Lua code into your R package, either for internal use within the package or for direct use by your package’s users.

Generally when you want to include Lua code in your package, it means that you want to include one or more functions written in Lua either internally in your package or as an outward-facing function for your users.

In Lua, the standard way to write a “module” – Lua’s equivalent of R’s packages – is to put together a Lua script that returns a table, where the table contains all the functions (or variables) that the module should export. The script can also contain local functions or variables that are accessible only within the module itself, and the module is then loaded with Lua’s require function. For example, you may have a script mymodule.lua containing this Lua code:

local mymodule = {}

local fave_name = "Nick"

function mymodule.greet(name)
    print("Hello, " .. name .. "!")
    if name == fave_name then
        print("Incidentally, that's a great name. Nice one.")
    end
end

return mymodule

This returns a Lua table containing the function greet, which in turn makes use of the “private” (local) variable fave_name. In Lua, to use this module, you would write something like:

local mymod = require "mymodule"
mymod.greet("Janet")

Here, Lua’s require looks for a script called mymodule.lua in a few places, including the current working directory. For more information on Lua modules, see the Lua wiki and the Lua manual.

You can use a similar technique to load functions from a Lua module into R as follows. The recommendation is that you put mymodule.lua in your package directory under the subdirectory inst/Lua; then when your package is installed, you can get the path to this module file using system.file("Lua", "mymodule.lua", package = "myPackage"), where "myPackage" is the name of your R package. Then create a new R script in the R subdirectory of your package, called e.g. lua_exports.R, with the following code:

# Package Lua state
pkgL <- NULL

#' Greet the user
#'
#' This function prints a greeting to someone.
#'
#' @usage greet(name)
#' @param name The name of the person to be greeted.
#' @return Nothing.
#' @export
greet <- NULL

.onLoad <- function(libname, pkgname)
{
    # Obtain the package namespace
    ns <- asNamespace(pkgname)

    # Create a Lua state for this package
    assign("pkgL", luajr::lua_open(), envir = ns)

    # Load the module in our package state
    mymod <- luajr::lua(filename = system.file("Lua", "mymodule.lua",
        package = "luajrtest"), L = pkgL)

    # Add functions to the package namespace
    assign("greet", luajr::lua_func(mymod$greet, "1", L = pkgL), envir = ns)

    invisible()
}

There are three sections here. First, we create the variable pkgL which will hold the Lua state used by the functions in our package.

Second, we create the variable greet which is a placeholder for the function greet within the Lua module. We document this object as though it was a function, with parameters defined. Note that we provide an explicit @usage tag because roxygen can’t guess the parameters just by inspecting the object’s declaration (which is NULL at this point). We also mark it for @export.

Finally, we write the .onLoad function which is automatically called whenever our package is loaded. (See the R documentation ?.onLoad for details.) Within this function, we create the Lua state in pkgL, load the module as a list mymod, and then assign to the variable greet using lua_func().

Note that mymod is a list containing the member greet, which is an external pointer to the Lua function. This pointer cannot be used directly as a function, but has to be passed through lua_func() so that we can specify the argcode argument which determines the function’s parameter passing behaviour.

For each new function you want to import from your Lua module, you will have to add a new “placeholder” (like greet above), (optionally) document the placeholder with details of how to call the function, mark the function for @export (if you want it to be available to end users of the package), and use assign to actually load the corresponding function into place.