5

Suppose I have a function in Idris that does some computation. For simplicity, let it be stringly typed for now.

f: String -> String

How can I compile this function to JavaScript so that it can then be called from any ordinary JavaScript code?

If that is too easy, suppose f, instead of String, deals with Double or even a custom Idris data type.

I know I can compile a whole module with a Main.main function and a more or less inscrutable blob of JavaScript will be output. Can I maybe extract my function from there by hand? How should I go about it?


P.S. Despite my answering myself, I am still looking for a better solution, so welcome.

Ignat Insarov
  • 4,660
  • 18
  • 37

2 Answers2

4

Using this example, it seems at least with the Node backend this is doable. I've marked interact as export and added a library descriptor:

module Main

import Data.String

f: Double -> Double
f x = x + 1

export interact: String -> String
interact s = let x = parseDouble s in
    case x of
         Nothing => "NaN"
         Just x => show (f x)

main: IO ()
main = do
    s <- getLine
    putStrLn (interact s)

lib : FFI_Export FFI_JS "" []
lib = Data String "String" $
      Fun interact "interact" $
      Fun main "main" $
      End

I have then compiled with the --interface flag (this fails with --codegen javascript...):

idris --codegen node --interface  --output ExportToJS.js ExportToJS.idr

and the resulting .js file has this at the end:

module.exports = {
interact: Main__interact,
main: Main__interact
};
}.call(this))

This should allow you to do require("./ExportToJavaScript.js").interact("42") from Node, and there is probably an equivalent to use from a browser.

Cactus
  • 27,075
  • 9
  • 69
  • 149
1

Yes, you can extract any function by hand.

  1. Build a module as follows:

    module Main
    
    import Data.String
    
    f: Double -> Double
    f x = x + 1
    
    interact: String -> String
    interact s = let x = parseDouble s in
        case x of
             Nothing => "NaN"
             Just x => show (f x)
    
    main: IO ()
    main = do
        s <- getLine
        putStrLn (interact s)
    
  2. Compile it as follows:

    % idris --codegen javascript --output Main.js Main.idr
    

    A file called Main.js will be created. There will be several megabytes of more or less inscrutable JavaScript code, just as you say.

  3. Edit this file by hand and edit it similarly to this:

    --- Resistors.js
    +++ Resistors-default.js
    @@ -1,7 +1,5 @@
     "use strict";
    
    -(function(){
    -
     const $JSRTS = {
         throw: function (x) {
             throw x;
    @@ -36130,7 +36128,3 @@
             }
         }
     }
    -
    -
    -$_0_runMain();
    -}.call(this))
    
  4. Now notice this JS file has comments in it marking the JS functions with their Idris names. For instance, corresponding to our interact function there will be located this JS function:

    // Main.interact
    
    function Main__interact($_0_arg){
        const $_1_in = Data__String__parseDouble($_0_arg);
    
        if(($_1_in.type === 1)) {
            const $cg$3 = Main__bestMatch_39_($_1_in.$1, Main__manyResistors_39_());
            let $cg$2 = null;
            $cg$2 = $cg$3.$1;
            return Prelude__Show__Main___64_Prelude__Show__Show_36_Schema_58__33_show_58_0($cg$2);
        } else {
            return "NaN";
        }
    }
    
  5. If you attach this JS file to a web page as a script, you may then open JS console in a browser and interact with your Idris functions, like this:

    Main__interact("10")
    "11"
    

Hope this helps!

Ignat Insarov
  • 4,660
  • 18
  • 37