PureScript FFI ↔ JavaScript Promises: Automatic Conversion?
0 reputation · 05 Nov 2021, 09:14 UTC
0 reputation · 05 Nov 2021, 09:14 UTC
We want a seamless way to call a JavaScript function that returns a Promise from PureScript, automatically mapping it to PureScript’s Effect or Async monad.
The foreign import syntax treats imported values as raw JavaScript, so a Promise returned by the imported function is passed through untouched. There is no automatic conversion; developers must wrap the Promise manually with Effect.async or Async.fromEffect, adding boilerplate and error handling concerns. This lack of a unified strategy leads to inconsistent patterns across codebases.
Should the FFI layer provide an automatic mapping of Promise-returning functions to Async (or Effect), or should it continue to expose raw Promises and leave conversion to the developer?
33775 reputation · 05 Nov 2021, 19:35 UTC
PureScript's foreign import does not automatically convert JavaScript Promises to Effect or an async monad. The compiler binds the JavaScript value directly and does not inspect runtime shapes. A Promise returned from an imported function remains a raw JavaScript object in your PureScript type system.
Effect cannot represent a PromiseEffect is synchronous. It models computations that complete before returning control to PureScript. A JavaScript Promise settles asynchronously, so mapping it to Effect a would require either blocking the JavaScript event loop (which PureScript cannot do) or returning before settlement. Neither is possible. The correct target is an asynchronous effect type, not Effect.
Confirmed: foreign import only binds a JavaScript value. The compiler does not inspect runtime shapes. A Promise is just a JS object, so the boundary must be typed explicitly, for example as returning Effect (Promise a).
Likely explanation: The absence of automatic conversion is deliberate. Automatic conversion would impose one global policy for rejection, cancellation, and laziness. JS rejection is not a PureScript exception; converting it to an async effect usually yields a generic error unless you wrap failures in Either or a custom error type.
Effect (Promise a) or Effect (Promise Foreign).Either e a.If FFI later auto-converted, existing imports typed Effect (Promise a) and manual wrappers would change meaning or break. A typeclass or annotation could be added as a library convention, but it cannot be inferred safely without runtime tags and still needs explicit instances and an error policy.
Which async-effect library and helper names does your codebase use? The recommendation to keep the boundary raw and convert explicitly does not change, but the exact conversion code depends on which async monad you target.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.