简体   繁体   English

如何使用GHCJS从Javascript调用Haskell

[英]How to call Haskell from Javascript with GHCJS

I've been playing about with GHCJS. 我一直在玩GHCJS。 The FFI can be used to call javascript from Haskell but I can't figure out how do go the other way round. FFI可用于从Haskell调用javascript,但我无法弄清楚如何反过来。 Say I had a super useful utility function I wrote in Haskell: 假设我在Haskell中编写了一个超级有用的实用函数:

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

Is it possible do something so I could call it from Javascript? 有可能做一些事情,所以我可以从Javascript调用它吗? The closest I've got is noticing that h$main(h$main2CMainzimain) will trigger my Haskell main function. 我最接近的是注意到h$main(h$main2CMainzimain)将触发我的Haskell主函数。

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 . 为了导出它,我们需要通过GHCJS.Foreign一个*Callback函数使其成为回调函数。 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: 我的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

Finally, we need a little un-wrapper on the JS side: 最后,我们需要JS方面的一些un-wrapper:

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

and now we can use our nice Haskell-implemented function: 现在我们可以使用我们很好的Haskell实现的函数:

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. JsInterface.hs ,其被定义为main-in所述的executable卡瓦尔文件中, main功能设置全局Java脚本变量incredibleLogic_ ,而JavaScript的胶水代码负责包装和拆包的参数。

Here's an example that shows how to call a Haskell function from Javascript. 这是一个示例,演示如何从Javascript调用Haskell函数。 This is similar to the example provided by Joachim but compiles and runs with the latest ghcjs. 这类似于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"

The test works by calling from Haskell into Javascript code that then calls back into Haskell. 测试通过从Haskell调用Javascript代码然后调用回Haskell来工作。 The variable, "js_callback_", becomes available within Javascript for use as a function that takes one string argument. 变量“js_callback_”在Javascript中可用作带有一个字符串参数的函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM