Here is a way to make it work. Assume we have some useful function, like
revString :: String -> String
revString = reverse
somethingUseful :: JSString -> IO JSString
somethingUseful = return . toJSString . revString . fromJSString
In order to export that, we need to make it a callback via one of the *Callback functions in GHCJS.Foreign. But these would discard the return value, so we need a wrapper that puts the result into a second argument:
returnViaArgument :: (JSRef a -> IO (JSRef b)) -> JSRef a -> JSRef c -> IO ()
returnViaArgument f arg retObj = do
r <- f arg
setProp "ret" r retObj
My main function creates the callback, and saves it as something that’s global to JavaScript:
foreign import javascript unsafe "somethingUseful_ = $1"
js_set_somethingUseful :: JSFun a -> IO ()
main = do
callback <- syncCallback2 NeverRetain False (returnViaArgument somethingUseful)
js_set_somethingUseful callback
Finally, we need a little un-wrapper on the JS side:
function somethingUseful (arg) {x = {}; somethingUseful_(arg, x); return x.ret};
and now we can use our nice Haskell-implemented function:
somethingUseful("Hello World!")
"!dlroW olleH"
I am using this trick in a real-world application. In JsInterface.hs, which is defined as main-in of the executable in the Cabal file, the main function sets the global java script variable incredibleLogic_, while the JavaScript glue code takes care of packing and unpacking the parameters.
var haskell = {}, export = functon (name, val) { haskell[name] = val; };and then bringexportinto Haskell via FFI,export "sayHello" sayHelloshould potentially sethaskell.sayHelloto be whatever the function is, without weirdh$main()variables littering everything.