GHC Wasm FFI Compilation

Hello,

i am playing around with the GHC WASM build feature. I have checked out the ghc-wasm-miso-example and had no problems to compile and run.
Now, I would like to test the FFI and javascript external libs communication (for instance using local storage). This is what I tried:

import GHC.Wasm.Prim (JSString, fromJSString, toJSString)

foreign import javascript safe "console.log($1);"
  js_log :: JSString -> IO ()

jsLog :: JSString -> IO ()
jsLog = js_log

According to the documentation this should work. But when I try to compile the example application which contains this code snippet, I get a compilation error.

/var/folders/jp/nwqmmxhs3fs_60k1_tf69n4c0000gn/T/nix-shell.f6XwHN/ghc51844_tmp_5_0/ghc_tmp_5_2.c:16:63: error:
     error: unknown type name 'HsJSString'
       16 | HsJSVal ZC0ZChs2048zm0zi1zi0zi0zminplaceZCTwoZZeroFourEightZC(HsJSString a1);
          |                                                               ^
   |
16 | HsJSVal ZC0ZChs2048zm0zi1zi0zi0zminplaceZCTwoZZeroFourEightZC(HsJSString a1);
   |                                                               ^

/var/folders/jp/nwqmmxhs3fs_60k1_tf69n4c0000gn/T/nix-shell.f6XwHN/ghc51844_tmp_5_0/ghc_tmp_5_2.c:18:92: error:
     error: call to undeclared function 'rts_getJSString'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
       18 | {return rts_mkJSVal(&MainCapability, ZC0ZChs2048zm0zi1zi0zi0zminplaceZCTwoZZeroFourEightZC(rts_getJSString(a1)));}
          |                                                                                            ^
   |
18 | {return rts_mkJSVal(&MainCapability, ZC0ZChs2048zm0zi1zi0zi0zminplaceZCTwoZZeroFourEightZC(rts_getJSString(a1)));}
   |                                                                                            ^

/var/folders/jp/nwqmmxhs3fs_60k1_tf69n4c0000gn/T/nix-shell.f6XwHN/ghc51844_tmp_5_0/ghc_tmp_5_2.c:18:92: error:
     note: did you mean 'rts_mkString'?
   |
18 | {return rts_mkJSVal(&MainCapability, ZC0ZChs2048zm0zi1zi0zi0zminplaceZCTwoZZeroFourEightZC(rts_getJSString(a1)));}
   |                                                                                            ^

/nix/store/bdfdpn4pxfg96vxzzz11lg4hzlb2d9p4-wasm32-wasi-ghc-9.12/lib/wasm32-wasi-ghc-9.12.2.20250327/lib/../lib/wasm32-wasi-ghc-9.12.2.20250327-inplace/rts-1.0.2/include/RtsAPI.h:464:14: error:
     note: 'rts_mkString' declared here
      464 | HaskellObj   rts_mkString     ( Capability *, char    *s );
          |              ^
    |
464 | HaskellObj   rts_mkString     ( Capability *, char    *s );
    |              ^

2 errors generated.
<no location info>: error:
    `wasm32-wasi-clang' failed in phase `C Compiler'. (Exit code: 1)

Error: [Cabal-7125]
Failed to build hs2048-0.1.0.0 (which is required by exe:ghc-wasm-miso-examples from ghc-wasm-miso-examples-0).

Any idea or advice of what I am probably missing here?

Thanks a lot!
Alex

1 Like

The GHC Wasm JSFFI only supports (apart from primitive types) JSVal and newtypes on top of JSVal, such as JSString. The issue here is that GHC doesn’t see that JSString is a newtype of JSVal, so it complains. (The error message could certainly be improved!)

The solution is to import the constructor of JSString:

import GHC.Wasm.Prim (JSString(..), fromJSString, toJSString)

Then your example will compile.


Note that this is analogous to “regular” C FFI: Compiling

import Foreign.C.Types (CInt)

foreign import capi "abs" absss :: CInt -> CInt

will fail with

    • Unacceptable result type in foreign declaration:
        ‘CInt’ cannot be marshalled in a foreign call
          because its data constructor is not in scope
    • When checking declaration:
        foreign import capi safe "abs" absss :: CInt -> CInt
    Suggested fix: Import the data constructor ‘CInt’ of ‘CInt’
   |
10 | foreign import capi "abs" absss :: CInt -> CInt
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4 Likes

Thanks a lot, it is not very obvious :slight_smile: