23

我一直在玩 GHCJS。FFI 可用于从 Haskell 调用 javascript,但我不知道如何反过来。假设我有一个在 Haskell 中编写的超级有用的实用程序函数:

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

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

4

2 回答 2

11

这是使其工作的一种方法。假设我们有一些有用的功能,比如

revString :: String -> String
revString = reverse

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

为了导出它,我们需要通过*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

我的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

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

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

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

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

我在现实世界的应用程序中使用了这个技巧。在Cabal 文件中定义的JsInterface.hs中,该函数设置全局 java 脚本变量,而JavaScript 胶水代码负责打包和解包参数。main-inexecutablemainincredibleLogic_

于 2015-07-24T13:05:31.520 回答
10

这是一个示例,展示了如何从 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"

测试通过从 Haskell 调用 Javascript 代码,然后再调用 Haskell 来进行。变量“js_callback_”在 Javascript 中可用作接受一个字符串参数的函数。

于 2015-12-18T03:14:42.763 回答