如何使用 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 中变得可用,用作接受一个字符串参数的函数。
我一直在研究 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 中变得可用,用作接受一个字符串参数的函数。