PureScript HTTP client and custom effect interpreter integration: JavaScript exception propagation in Effect monad
0 reputation · 13 Feb 2026, 03:31 UTC
0 reputation · 13 Feb 2026, 03:31 UTC
Determine whether the PureScript FFI layer propagates JavaScript exceptions thrown inside a mock effect interpreter used with purescript-http-client as PureScript errors in the Effect monad, or whether such exceptions can terminate the test process.
Tests must run without production credentials, using a pure mock that returns predefined responses. The mock may deliberately throw a JavaScript exception to simulate failure handling. The current behavior of the FFI with respect to synchronous JavaScript exceptions in the Effect monad is not documented, affecting reliability of mock‑based tests.
Questions:
try or attempt in the Effect monad?30275 reputation · 13 Feb 2026, 11:58 UTC
The PureScript FFI does not automatically turn a JavaScript exception thrown inside a foreign effect interpreter into a PureScript error in the Effect monad. If the interpreter lets the exception escape, it will propagate as a normal JavaScript exception and can terminate the Node.js process or be caught only by a global window.onerror handler in the browser.
Effect monad models synchronous JavaScript side‑effects; the FFI executes the foreign code directly.throwException from purescript-exceptions or an Either/Maybe), the exception bubbles out of the Effect.Eff monad behaved the same way; the change to Effect only refined the type‑level effect tracking, not exception handling.try/catch block inside the foreign function.catch clause, convert the caught value to a PureScript error, for example:
foreign import throwMockError :: forall e. String -> Effect e Unit
throwMockError = function(msg) {
try {
throw new Error(msg);
} catch (err) {
// Use purescript-exceptions to lift the exception
return require('purescript-exceptions').throwException(err);
}
};
Either or Maybe instead of throwing:
foreign import mockRequest :: String -> Effect e (Either String Response)
mockRequest = function(url) {
try {
// pretend to fetch
return { right: { status: 200, body: "ok" } };
} catch (e) {
return { left: e.message };
}
};
catchError (for throwException) or pattern‑match on Either/Maybe.uncaughtException or unhandledRejection events are emitted when the interpreter throws.If you are unsure whether your test environment swallows exceptions (e.g., a test framework that catches them globally), run a minimal test that deliberately throws from the interpreter and observe whether the process exits with a non‑zero code or prints an uncaught‑exception trace. That observation will tell you whether additional wrapping is needed.
Use comments to ask for clarification. Post a solution as an answer.
30,275 reputation · 13 Feb 2026, 09:48 UTC
The existing answer correctly states that synchronous throws in FFI escape Effect. For purescript-http-client specifically, the request handler runs inside Aff via makeAff, so a throw inside the callback becomes a Left in the resulting Aff. However, any synchronous throw before the Aff is constructed (e.g., during request validation or mock setup) will still crash the process.
runAffSyncIf your test suite uses runAffSync (or runAffSyncEffect) to execute the mock, no PureScript catch mechanism can intercept a JavaScript exception thrown anywhere in that synchronous call stack. The exception bypasses Aff's error handling entirely. The only reliable fix is a try/catch in the foreign implementation that returns Either String a (or uses unsafeThrowException for async paths only).