You’re One JavaScript Function Call Away From Using (Most) WebR R Functions In Your WebR-Powered Apps/Sites

After writing the initial version of a tutorial on wrapping and binding R functions on the javascript side of WebR, I had a number of other WebR projects on the TODO list. But, I stuck around noodling on the whole “wrapping & binding” thing, and it dawned on me that there was a “pretty simple” way to make R functions available to javascript using javascript’s Function() constructor. I’ll eventually get this whole thing into the GH webr-experiments repo, but you can see below and just view-source (it’s as of the time of this post, in the 130’s re: line #’s) to check it out before that.

Dynamic JavaScript Function Creation

The Function() constructor has many modes, one of which involves providing the whole function source and letting it build a function for you. R folks likely know you can do that in R (ref: my {curlconverter} pkg), and it’s as straightforward on the javascript side. Open up DevTools Console and enter this:

const sayHello = new Function('return function (name) { return `Hello, ${name}` }')();

then call the function with some string value.

We only need one input to create a dynamic R function wrapper (for SUPER BASIC R functions ONLY). Yes, I truly mean that you can just do this:

let rbeta = await wrapRFunction("rbeta");
await rbeta(10, 1, 1, 0)

and get back the result in the way WebR’s toJs() function returns R objects:

{
  "type": "double",
  "names": null,
  "values": [
    0.9398577840605595,
    0.42045006265859153,
    0.26946718094298633,
    0.3913958406551122,
    0.8123499099597378,
    0.49116132695862963,
    0.754970193716774,
    0.2952198011408607,
    0.11734111483990002,
    0.6263863870230043
  ]
}

Formal Attire

R’s formals() function will let us get the argument list to a function, including any default values. We won’t be using them in this MVP version of the auto-wrapper, but I see no reason we couldn’t do that in a later iteration. It’s “easy” to make that a javascript function “the hard way”:

async function formals(rFunctionName) {
  let result = await globalThis.webR.evalR(`formals(${rFunctionName})`);
  let output = await result.toJs();
  return(Promise.resolve(output))
}

Now, we just need to use those names to construct the function. This is an ugly little function, but it’s really just doing some basic things:

async function wrapRFunction(rFunctionName) {
  let f = await formals(rFunctionName)
  let argNames = f.names.filter(argName => argName != '...');
  let params = argNames.join(', ')
  let env = argNames.map(name => `${name}: ${name}`).join(', ');
  let fbody =
  `return async function ${rFunctionName}(${params}) {
    let result = await globalThis.webR.evalR(
      '${rFunctionName}(${params})',
      { env: { ${env} }}
   );
    let output = await result.toJs();
    globalThis.webR.destroy(result);
    return Promise.resolve(output);
  }`;
  return new Function(fbody)()
}
  • first, we get the formals for the function name provided to us
  • then we remove ones we can’t handle (yet!)
  • then we take the R function parameter names and make two objects:
    • one that will make a comma-separated parameter declaration list (e.g. param1, param2, param3)
    • another that does the same, but as key: value pairs to pass as the environment (see previous WebR blog posts and experiments)
  • the function body is another template string that just makes the standard WebR set operations to evaluate an R object and get the value back.

We pass the built function string to the Function() constructor, and we have an automagic javascript wrapper for it.

FIN

This is a very naive wrapper. It’s all on the caller to specify all the values. R has some fun features, like the ability to not specify a value for a given parameter, and then check on the R side to see if it’s missing. It can also intermix named and positional parameters, and then there’s the dreaded ....

I’m going to noodle a bit more on how best to account and/or implement ^^, but I’ll 100% be using this idiom to wrap R functions as I keep experimenting.

One more thing: I had to abandon the use of the microlight syntax highlighter. It’s super tiny and fast, but it doesn’t handle the code I need, so I’ll be updating my nascent webr-template to incorporate what I am using now: Shiki. The next release will also include these basic wrapper functions.

Cover image from Data-Driven Security
Amazon Author Page

1 Comment You’re One JavaScript Function Call Away From Using (Most) WebR R Functions In Your WebR-Powered Apps/Sites

  1. Pingback: You’re One JavaScript Function Call Away From Using (Most) WebR R Functions In Your WebR-Powered Apps/Sites - Ciberdefensa

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.