22

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 function I wrote in Haskell:

sayHello :: String -> IO ()
sayHello name = print $ "hello, " ++ name

Is it possible do something so I could call it from Javascript? The closest I've got is noticing that h$main(h$main2CMainzimain) will trigger my Haskell main function.

2
  • 1
    if this still applies it will not be pretty - if you want that direction fay might be a better choice Commented Apr 30, 2015 at 12:14
  • If worse comes to worse you could potentially use the FFI to solve the problem. So if you have var haskell = {}, export = functon (name, val) { haskell[name] = val; }; and then bring export into Haskell via FFI, export "sayHello" sayHello should potentially set haskell.sayHello to be whatever the function is, without weird h$main() variables littering everything. Commented Apr 30, 2015 at 13:44

2 Answers 2

13

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.

Sign up to request clarification or add additional context in comments.

9 Comments

Do you happen to have this somewhere in github? So I could compile it using GHCJS ?
What is the return value here ? github.com/nomeata/incredible/blob/master/logic/js/… Where is incredibleLogic_ coming from here github.com/nomeata/incredible/blob/master/logic/js/… ?
Sorry for the brief comment, I was on a mobile phone. I now elaborated on it in the answer itself – does it help?
So, calling js_set_somethingUseful in main.hs causes somethingUseful_ to appear in the Javascript global namespace ? Do I understand it correctly?
Yes; have a close look at the foreign import javascript unsafe declaration: It compiles to the JS code "incredibleLogic_ = $1".
|
11

Here's an example that shows how to call a Haskell function from Javascript. This is similar to the example provided by Joachim but compiles and runs with the latest ghcjs.

import GHCJS.Marshal(fromJSVal)
import GHCJS.Foreign.Callback (Callback, syncCallback1, OnBlocked(ContinueAsync))
import Data.JSString (JSString, unpack, pack)
import GHCJS.Types (JSVal)

sayHello :: String -> IO ()
sayHello name = print $ "hello, " ++ name

sayHello' :: JSVal -> IO ()
sayHello' jsval = do
    Just str <- fromJSVal jsval
    sayHello $ unpack str

foreign import javascript unsafe "js_callback_ = $1"
    set_callback :: Callback a -> IO ()

foreign import javascript unsafe "js_callback_($1)" 
    test_callback :: JSString -> IO ()

main = do
    callback <- syncCallback1 ContinueAsync sayHello'
    set_callback callback
    test_callback $ pack "world"

The test works by calling from Haskell into Javascript code that then calls back into Haskell. The variable, "js_callback_", becomes available within Javascript for use as a function that takes one string argument.

12 Comments

Which version of GHCJS did you use? I get these errors: Module ‘GHCJS.Marshal’ does not export ‘fromJSVal’, Module ‘GHCJS.Types’ does not export ‘JSVal’ when I try to compile the above code. I'm using ghcjs-0.2.0.20151001_ghc-7.10.2.
dave@6d322ff6c63d:~$ ghcjs --version The Glorious Glasgow Haskell Compilation System for JavaScript, version 0.2.0 (GHC 7.10.3)
That is very curious. Any ideas as to what might cause us to have different modules if we both have GHCJS-0.2.0?
Not offhand. I can look into this in more detail in a few hours.
The above example is available in github : github.com/dc25/ghcjsCallback . I added a branch, nodejs, that compiles to code that runs under node.js : github.com/dc25/ghcjsCallback/tree/nodejs . This branch contains a haskell -> javascript -> haskell calling sequence in which the javascript is in a javascript only source file, Main.jsexe/tc.js, The callback to haskell is done via the js_callback_ variable. It was not necessary to export js_callback_ to access it outside of all.js .
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.