Admin message

Due to a large amount of spam we do not allow new users to create repositories, they are "external" users. If you are a new user and want to create a repository, for example for forking GHC, open a new issue on ghc/ghc using the "get-verified" issue template

JavaScript: implement callbacks in the FFI
https://gitlab.haskell.org/ghc/ghc/-/merge_requests/10128 # Introduction GHCJS previously implemented a `Callback a` data type, which is currently missing from GHC's JavaScript backend. This type represents functions passable in the FFI that use a more standard JavaScript calling convention - rather than the calling conventions used in the JS generated code. GHCJS supported functions up to 3 arity, and it supported various synchronicities of callbacks. Arguments are passed as an untyped `JSVal` - essentially a representation of a plain JavaScript value. # Usage To construct callbacks, the following functions are used depending on synchronisation and the existence (or lack thereof) of a return value: ``` data OnBlocked = ContinueAsync | ThrowWouldBlock asyncCallback1 :: (JSVal -> IO ()) -> IO (Callback (JSVal -> IO ())) syncCallback1 :: OnBlocked -> (JSVal -> IO ()) -> IO (Callback (JSVal -> IO ())) syncCallback1' :: (JSVal -> IO JSVal) -> IO (Callback (JSVal -> IO JSVal)) ``` Only the 1-arity signatures are demonstrated here, but sizes up to 3 - including 0 - are available. These are expected to be used as a way to call into Haskell-generated code from regular JavaScript, via FFI imports. A simple example demonstrating this is as follows: ``` foreign import javascript "((f,x) -> { f(x); })" js_apply :: Callback (JSVal -> IO ()) -> JSVal -> IO () fn x = if isNull x then putStrLn "isNull" else fromJSString x main = do f <- syncCallback1 ThrowWouldBlock fn js_apply f (toJSString "example!") releaseCallback f -- Memory can't be automatically freed because we don't know if the function is being held on the JavaScript side ``` # Callbacks as FFI "exports" Callbacks enable a form of FFI "exports", through setting global variables: ``` foreign import javascript "((f) => { globalF = f; })" setF :: Callback (JSVal -> IO ()) -> IO () fn x = if isNull x then putStrLn "isNull" else fromJSString x main = setF =<< syncCallback1 ThrowWouldBlock fn ``` Then, if our generated Haskell-main is called in the HTML head, `globalF` will be available as a callback for e.g. HTML buttons. # Implementation The `GHCJS.Foreign.Callback` module from the `ghcjs-base` package ports with minimal changes to the module and without changes to the current JavaScript backend (except for a few bugfixes that have already been merged).
issue