haskell ghcjs

Cómo llamar a Haskell desde Javascript con GHCJS



(2)

He estado jugando con GHCJS. El FFI se puede utilizar para llamar a javascript desde Haskell, pero no puedo averiguar cómo se hace al revés. Digamos que tenía una función de utilidad super útil que escribí en Haskell:

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

¿Es posible hacer algo para poder llamarlo desde Javascript? Lo más cercano que tengo es notar que h$main(h$main2CMainzimain) activará mi función principal de Haskell.


Aquí hay un ejemplo que muestra cómo llamar a una función de Haskell desde Javascript. Esto es similar al ejemplo proporcionado por Joachim pero compila y ejecuta con los últimos 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"

La prueba funciona al llamar de Haskell al código Javascript que luego vuelve a llamar a Haskell. La variable, "js_callback_", está disponible dentro de Javascript para usarla como una función que toma un argumento de cadena.


Aquí hay una manera de hacerlo funcionar. Supongamos que tenemos alguna función útil, como

revString :: String -> String revString = reverse somethingUseful :: JSString -> IO JSString somethingUseful = return . toJSString . revString . fromJSString

Para exportar eso, necesitamos hacer una devolución de llamada a través de una de las *Callback Funciones de GHCJS.Foreign *Callback en GHCJS.Foreign . Pero estos descartarían el valor de retorno, por lo que necesitamos una envoltura que ponga el resultado en un segundo argumento:

returnViaArgument :: (JSRef a -> IO (JSRef b)) -> JSRef a -> JSRef c -> IO () returnViaArgument f arg retObj = do r <- f arg setProp "ret" r retObj

Mi función main crea la devolución de llamada y la guarda como algo que es global para 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

Finalmente, necesitamos un poco de envoltura en el lado de JS:

function somethingUseful (arg) {x = {}; somethingUseful_(arg, x); return x.ret};

y ahora podemos usar nuestra buena función implementada por Haskell:

somethingUseful("Hello World!") "!dlroW olleH"

Estoy usando este truco en una aplicación del mundo real. En JsInterface.hs , que se define como main-in del executable en el archivo Cabal , la función main establece la variable de script java global incredibleLogic_ , mientras que el código de pegamento de JavaScript se encarga de empaquetar y desempaquetar los parámetros.