I found a number of issues in the FFI blog post. I am guessing it will be easier to fix them if I just enumerate them in the order in which they appear, rather than following the strict "Describe the bug / To Reproduce / Expected Behaviour" template. Please let me know if you'd prefer separate tickets and proposed fixes following that template.
The blog post refers to the type JSRef several times, but I believe this type has since been renamed to JSVal.
Missing import in the "Haskell calling JavaScript" code snippet:
If you would like to try this at home, put the above snippet in a file called jsffi.hs in the current directory.
However, if we follow these instructions and replace the JSRef with JSVal, we get the error Not in scope: type constructor or class ‘JSVal’, because the code snippet is missing the line import Asterius.Types.
First broken link in the "Haskell calling JavaScript" section:
Then, you can invoke ahc-link to compile it to .wasm/.js files (using the pre-built Asterius Docker images, as explained in the Asterius documentation): [...]
Second broken link in the "Haskell calling JavaScript" section:
Our current implementation supports a variety of primitive types, such as, Bool, Char, and Int as argument and result types of imported functions. See the reference documentation for a full list.
Regular Haskell value types like Int, Ptr, StablePtr, etc.
An easy fix would be to change the text to see "See the reference documentation for more information" instead of "for a full list", but a much better fix would be to change https://asterius.netlify.app/jsffi.html#directly-marshalable-value-types to provide a full list, as it's not at all obvious what this etc. stands for.
Outdated flag and code snippet in the "JavaScript calling Haskell":
The tool ahc-link provides a flag --asterius-instance-callback=, which takes a JavaScript callback function, which is to be called once an Asterius instance has been successfully initiated. [...] Continuing with the above example, in order to call mult_hs in JavaScript, the callback that we need to supply would be:
i => {
i.wasmInstance.exports.hs_init();
console.log(i.wasmInstance.exports.mult_hs(6, 7));
}
However, ahc-link no longer supports the --asterius-instance-callback= flag, the exported haskell functions are no longer nested inside a wasmInstance field, the hs_init call no longer seems necessary, and mult_hs now returns the Promise of an Int rather than an Int. This section of the blog post thus needs some major changes. Here is a suggested rewrite:
By default, the tool ahc-link generates a file <HaskellFileName>.mjs which loads the generated wasm code, creates an Asterius instance named i, and calls i.exports.main() in order to run the Haskell file's main function:
import * as rts from "./rts.mjs";
import module from "./<HaskellFileName>.wasm.mjs";
import req from "./<HaskellFileName>.req.mjs";
module
.then(m => rts.newAsteriusInstance(Object.assign(req, { module: m })))
.then(i => {
i.exports.main();
});
This default setup assumes that your program's entry point is your Haskell file's main function. If you instead want your program's entry point to be on the JavaScript side, you can write your own mjs file which does something different. Here is one which calls our exported mult_hs function:
$ cat Example.hs
module Example where
foreign export javascript "mult_hs" (*) :: Int -> Int -> Int
$ cat Example.mjs
import * as rts from "./rts.mjs";
import module from "./Example.wasm.mjs";
import req from "./Example.req.mjs";
module
.then(m => rts.newAsteriusInstance(Object.assign(req, { module: m })))
.then(i => i.exports.mult_hs(6, 7))
.then(r => {
console.log("6 * 7 = ", r);
});
$ ahc-link --input-hs=Example.hs --no-main --export-function=mult_hs --input-mjs=Example.mjs --run
[...]
6 * 7 = 42
Outdated types and code snippet in "Using Haskell closures as JavaScript callbacks" section.
The section talks about StablePtr and makeHaskellCallback, but makeHaskellCallback has been replaced with wrapper, which has a simpler API which doesn't require StablePtr. Also, the beforeExit example loops forever, because a JSFunction now returns a Promise, and scheduling a Promise inside a beforeExit handler causes the Node.js process not to exit after all, but instead to wait for that Promise to complete, at which point the beforeExit handler is called again, etc. This section of the blog post is thus also in need of some major changes. Here is a suggested rewrite:
One limitation of the foreign export javascript approach is that it is only possible to export top-level definitions. Asterius also supports dynamically converting a Haskell function into a form which can be called from the JavaScript side. Here is a simple example:
On the JavaScript side, the JSFunction can be called like a regular JavaScript function which returns a Promise. wrapper is also able to wrap functions of multiple arguments as well as IO actions.
The name makeHaskellCallback is still mentioned at the end of the RTS documentation, even though the new name is now wrapper.
I found a number of issues in the FFI blog post. I am guessing it will be easier to fix them if I just enumerate them in the order in which they appear, rather than following the strict "Describe the bug / To Reproduce / Expected Behaviour" template. Please let me know if you'd prefer separate tickets and proposed fixes following that template.
The blog post refers to the type
JSRef
several times, but I believe this type has since been renamed toJSVal
.Missing import in the "Haskell calling JavaScript" code snippet:
However, if we follow these instructions and replace the
JSRef
withJSVal
, we get the errorNot in scope: type constructor or class ‘JSVal’
, because the code snippet is missing the lineimport Asterius.Types
.First broken link in the "Haskell calling JavaScript" section:
"Asterius documentation" links to https://tweag.github.io/asterius/, which 404's. Should probably link to https://asterius.netlify.app/images.html instead.
Second broken link in the "Haskell calling JavaScript" section:
"reference documentation" links to https://tweag.github.io/asterius/jsffi/, which 404's. Should probably link to https://asterius.netlify.app/jsffi.html#directly-marshalable-value-types instead.
If the previous link is indeed changed to https://asterius.netlify.app/jsffi.html#directly-marshalable-value-types, then this introduces a new issue. The text surrounding the link promises that the linked documentation provides the full list of supported primitive types, but https://asterius.netlify.app/jsffi.html#directly-marshalable-value-types is also limited to a partial list:
An easy fix would be to change the text to see "See the reference documentation for more information" instead of "for a full list", but a much better fix would be to change https://asterius.netlify.app/jsffi.html#directly-marshalable-value-types to provide a full list, as it's not at all obvious what this
etc.
stands for.Outdated flag and code snippet in the "JavaScript calling Haskell":
However,
ahc-link
no longer supports the--asterius-instance-callback=
flag, the exported haskell functions are no longer nested inside awasmInstance
field, thehs_init
call no longer seems necessary, andmult_hs
now returns the Promise of an Int rather than an Int. This section of the blog post thus needs some major changes. Here is a suggested rewrite:Outdated types and code snippet in "Using Haskell closures as JavaScript callbacks" section.
The section talks about
StablePtr
andmakeHaskellCallback
, butmakeHaskellCallback
has been replaced withwrapper
, which has a simpler API which doesn't requireStablePtr
. Also, thebeforeExit
example loops forever, because aJSFunction
now returns a Promise, and scheduling a Promise inside abeforeExit
handler causes the Node.js process not to exit after all, but instead to wait for that Promise to complete, at which point thebeforeExit
handler is called again, etc. This section of the blog post is thus also in need of some major changes. Here is a suggested rewrite:The name
makeHaskellCallback
is still mentioned at the end of the RTS documentation, even though the new name is nowwrapper
.