Подтвердить что ты не робот

Как вызвать Haskell из Javascript с помощью GHCJS

Я играл с GHCJS. FFI можно использовать для вызова javascript из Haskell, но я не могу понять, как идти в другую сторону. Скажем, у меня была супер полезная функция полезности, которую я написал в Haskell:

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

Возможно ли сделать что-то, чтобы я мог назвать это из Javascript? Самое близкое, что у меня есть, замечает, что h$main(h$main2CMainzimain) вызовет мою основную функцию Haskell.

4b9b3361

Ответ 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 заботится об упаковке и распаковывании параметров.

Ответ 2

Вот пример, который показывает, как вызвать функцию 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 для использования в качестве функции, которая принимает один строковый аргумент.