Ответ 1
Вот как это сделать. Предположим, что у нас есть полезная функция, например
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
Наконец, нам нужна небольшая un-wrapper со стороны JS:
function somethingUseful (arg) {x = {}; somethingUseful_(arg, x); return x.ret};
и теперь мы можем использовать нашу замечательную функцию, реализованную в Haskell:
somethingUseful("Hello World!")
"!dlroW olleH"
Я использую этот трюк в реальном приложении. В JsInterface.hs, который определяется как main-in
executable
в Cabal file, функция main
задает глобальную переменную java script incredibleLogic_
, а код JavaScript для JavaScript заботится об упаковке и распаковывании параметров.