Как позвонить на Хаскель из Javascript с помощью GHCJS
Я играл с GHCJS. FFI можно использовать для вызова javascript из Haskell, но я не могу понять, как поступить наоборот. Скажем, у меня была супер полезная служебная функция, которую я написал на Haskell:
sayHello :: String -> IO ()
sayHello name = print $ "hello, " ++ name
Можно ли сделать что-то, чтобы я мог назвать это из Javascript? Самое близкое, что у меня есть, это замечание h$main(h$main2CMainzimain)
вызовет мою основную функцию на Haskell.
2 ответа
Вот способ заставить это работать. Предположим, у нас есть полезная функция, например,
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"
Я использую этот трюк в реальном приложении. В JsInterface.hs, который определяется как main-in
из executable
в файле Кабала, main
функция устанавливает глобальную переменную сценария Java incredibleLogic_
в то время как JavaScript-код склеивает заботу об упаковке и распаковке параметров.
Вот пример, который показывает, как вызвать функцию Haskell из Javascript. Это похоже на пример, предоставленный 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 для использования в качестве функции, которая принимает один строковый аргумент.