如何使用GHCJS从Javascript调用Haskell

Gar*_*ock 20 haskell ghcjs

我一直在玩GHCJS.FFI可用于从Haskell调用javascript,但我无法弄清楚如何反过来.假设我在Haskell中编写了一个非常实用的实用函数:

sayHello :: String -> IO ()
sayHello name = print $ "hello, " ++ name
Run Code Online (Sandbox Code Playgroud)

有可能做一些事情,所以我可以从Javascript调用它吗?我最接近的是注意到h$main(h$main2CMainzimain)会触发我的Haskell主函数.

Joa*_*ner 11

这是一种让它发挥作用的方法.假设我们有一些有用的功能,比如

revString :: String -> String
revString = reverse

somethingUseful :: JSString -> IO JSString
somethingUseful = return . toJSString . revString  . fromJSString
Run Code Online (Sandbox Code Playgroud)

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

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

我的main函数创建回调,并将其保存为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
Run Code Online (Sandbox Code Playgroud)

最后,我们需要JS方面的一些un-wrapper:

function somethingUseful (arg) {x = {}; somethingUseful_(arg, x); return x.ret};
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用我们很好的Haskell实现的函数:

somethingUseful("Hello World!")
"!dlroW olleH"
Run Code Online (Sandbox Code Playgroud)

我在实际应用程序中使用此技巧.在JsInterface.hs,其被定义为main-inexecutable卡瓦尔文件,该main功能设置全局Java脚本变量incredibleLogic_,而JavaScript的胶水代码负责包装和拆包的参数.


Dav*_*ton 8

这是一个示例,演示如何从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_ = $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"
Run Code Online (Sandbox Code Playgroud)

测试通过从Haskell调用Javascript代码然后调用回Haskell来工作.变量"js_callback_"在Javascript中可用作带有一个字符串参数的函数.

  • 上述示例可在 github 中找到:https://github.com/dc25/ghcjsCallback。我添加了一个分支nodejs,它编译为在node.js下运行的代码:https://github.com/dc25/ghcjsCallback/tree/nodejs。该分支包含 haskell -&gt; javascript -&gt; haskell 调用序列,其中 javascript 位于仅 javascript 源文件“Main.jsexe/tc.js”中,对 haskell 的回调是通过“js_callback_”变量完成的。无需导出 `js_callback_` 即可在 `all.js` 之外访问它。 (2认同)