IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

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

    hrbrmstr发表于 2023-03-21 17:40:55
    love 0
    [This article was first published on rud.is"In God we trust. All others must bring data"R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
    Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

    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.

    To leave a comment for the author, please follow the link and comment on their blog: rud.is"In God we trust. All others must bring data"R.

    R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
    Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
    Continue reading: You’re One JavaScript Function Call Away From Using (Most) WebR R Functions In Your WebR-Powered Apps/Sites


沪ICP备19023445号-2号
友情链接