I just published a new package elm-taskport, which allows to interact with effectful JavaScript functions using Elm’s Task abstraction in the same way Http.task does, instead of relying on ports. It uses age-old trick of monkey-patching XHR to redirect it to a local function, which has been attempted previously and is proven to work across many browsers.
The motivation for making this is to enable richer interactions with JavaScript, which could be chained together using Task.andThen and similar constructs. This is by no means a replacement for Elm ports, which are perfectly fine, but they have limits. In particular, implementing interop with ‘chatty’ JS APIs using ports is incredibly painful as it requires a lot of boilerplating and leads to overcomplicated models. TaskPort is an alternative to this building on a powerful Task API, and a considered application may reduce the complexity of the code.
TaskPort also works in Node.js by making XMLHttpRequest API available to the Elm runtime, so if you are using Elm in a worker sandbox on the server side, it can be used there as well.
Looking for community’s feedback on the package and additional features it might require.
For some bizzare reason, despite having 1.0.3 published and available using a direct link, I am still seeing 1.0.2 as latest on the packages website. You will get 1.0.3 installed if you make elm install lobanov/elm-taskport though
You can use elm-taskport for non-effectful JS as well, although to make it useful you are probably going to chain it with something effectful using Task.andThen.
If you are talking about a foreign function interface-style of interop with JS where you can call any JS function as you would call an Elm function, I don’t think it would be as useful as it seems. In Elm all functions are pure functions, which boils down to them being deterministic and referentially transparent, so your non-effectful JS functions must be deterministic and referentially transparent as well, otherwise you risking introducing unpredictable runtime behaviour. This means JS code must not interact with the browser environment or remote APIs, which effectively would mean you gain nothing from having that logic implemented in JS as opposed to Elm. Plus, because any interop mechanism with dynamically interpreted language is inherently unsafe, you would still need to have error handling around it, which would make it more cumbersome.
One situation it could be very useful is for measuring the size of text before rendering, which is sometimes necessary when doing SVG. Its a pure function, and can be computed with opentype.js. It would be very handy to be able to call this from Elm, since opentype.js is a complicated library - it could be ported to pure Elm but probably going to take someone a few months hard work to do it.
Previously I have used opentype.js through ports with Cmds, and its a pain since you need request/response ports and need to tag every request with an id to match up requests to responses, and also you need some states in your Model to track what requests are pending, and delay adding things to the view until they are computed. As much as the XHR hack bothers me, I would definitely consider using elm-taskport for this.