我一直在尝试使用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
函数之一将其变为回调。但这些会丢弃返回值,因此我们需要一个包装器,将结果放入第二个参数中:
为了导出它,我们需要通过 GHCJS.Foreign
中的 *Callback
函数之一将其变为回调。但是这些函数会丢弃返回值,因此我们需要一个包装器,将结果放入第二个参数中:
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"
我在一个真实世界的应用程序中使用了这个技巧。在JsInterface.hs中,它被定义为Cabal文件中executable
中的main-in
,main
函数设置全局javascript变量incredibleLogic_
,而JavaScript粘合代码则负责打包和解包参数。
incredibleLogic_
是从哪里来的?https://github.com/nomeata/incredible/blob/master/logic/js/js-interface-wrapper.js#L10 - jhegedusforeign import javascript unsafe
声明:它编译成 JS 代码 "incredibleLogic_ = $1"
。 - Joachim Breitner这里有一个示例,展示了如何从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代码,然后由Javascript回调到Haskell来实现。变量"js_callback_"在Javascript内部可用作一个函数,该函数接受一个字符串参数。
Module ‘GHCJS.Marshal’ does not export ‘fromJSVal’
,Module ‘GHCJS.Types’ does not export ‘JSVal’
。我正在使用 ghcjs-0.2.0.20151001_ghc-7.10.2
。 - WizekMain.jsexe/tc.js
中,对Haskell的回调通过js_callback_
变量完成。无需导出js_callback_
以在all.js
之外访问它。 - Dave Compton
var haskell = {},export = functon(name,val){ haskell [name] = val; };
,然后通过FFI将export
带入Haskell中,export“sayHello”sayHello
应该可以将haskell.sayHello
设置为任何函数,而不会出现奇怪的h$main()
变量混杂在其中。 - CR Drost