I\'ve been playing about with GHCJS. The FFI can be used to call javascript from Haskell but I can\'t figure out how do go the other way round. Say I had a super useful utility
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.