如何使用 GHCJS 从 Javascript 调用 Haskell

How to call Haskell from Javascript with GHCJS

我一直在研究 GHCJS。 FFI 可用于从 Haskell 调用 javascript,但我不知道如何反过来。假设我在 Haskell:

中写了一个超级有用的实用函数
sayHello :: String -> IO ()
sayHello name = print $ "hello, " ++ name

是否可以做一些事情以便我可以从 Javascript 调用它?我最接近的是注意到 h$main(h$main2CMainzimain) 将触发我的 Haskell 主函数。

这是一种让它工作的方法。假设我们有一些有用的函数,比如

revString :: String -> String
revString = reverse

somethingUseful :: JSString -> IO JSString
somethingUseful = return . toJSString . revString  . fromJSString

为了导出它,我们需要通过 GHCJS.Foreign 中的 *Callback 函数之一使其成为回调。但是这些会丢弃 return 值,所以我们需要一个将结果放入第二个参数的包装器:

returnViaArgument :: (JSRef a -> IO (JSRef b)) -> JSRef a -> JSRef c -> IO ()
returnViaArgument f arg retObj = do
    r <- f arg
    setProp "ret" r retObj

我的 main 函数创建回调,并将其保存为 JavaScript 的全局内容:

foreign import javascript unsafe "somethingUseful_ = "
    js_set_somethingUseful :: JSFun a -> IO ()

main = do
    callback <- syncCallback2 NeverRetain False (returnViaArgument somethingUseful)
    js_set_somethingUseful callback

最后,我们需要在 JS 端进行一些拆包:

function somethingUseful (arg) {x = {}; somethingUseful_(arg, x); return x.ret};

现在我们可以使用我们漂亮的 Haskell 实现函数:

somethingUseful("Hello World!")
"!dlroW olleH"

我在实际应用程序中使用了这个技巧。在 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 中负责打包和解包参数。

这是一个示例,说明如何从 Javascript 调用 Haskell 函数。这类似于 Joachim 提供的示例,但使用最新的 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_ = "
    set_callback :: Callback a -> IO ()

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

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

测试通过从 Haskell 调用到 Javascript 代码然后回调到 Haskell 来工作。变量 "js_callback_" 在 Javascript 中变得可用,用作接受一个字符串参数的函数。