Mastodon
Go back

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)()
}

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.


Join the conversation:follow@hrbrmstr@rud.ison the fediverse to read and reply to this post from your own instance.

No comments yet.


Previous Post
Linking Lit's Lightweight Web Components And WebR For Vanilla JS Reactivity
Next Post
WebR Filesystem Machinations & ReefR