How to call Haskell from Javascript with GHCJS

前端 未结 2 1303
耶瑟儿~
耶瑟儿~ 2021-02-01 19:00

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

2条回答
  •  感情败类
    2021-02-01 19:36

    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.

提交回复
热议问题