PureScript FFI and JavaScript module formats: verifying runtime representations across compiler versions
0 reputation · 29 Mar 2025, 19:37 UTC
0 reputation · 29 Mar 2025, 19:37 UTC
PureScript compiles to JavaScript and exposes host functions through foreign import, but the FFI boundary is untyped: PureScript's erased types become plain objects at runtime, and JavaScript consumers cannot rely on compile-time guarantees. The compiler can emit different JavaScript module formats, such as CommonJS and ES modules, which determines how Node.js, bundlers, and browser loaders resolve the generated output. Because default module formats and code-generation targets have shifted across compiler releases, build tooling pinned to an older PureScript version may produce output that a newer host runtime or bundler configuration no longer consumes as expected.
The unresolved behavior centers on runtime representation. Records and data constructors are documented as plain objects, yet newtype unwrapping and recursive types can produce layouts that surprise FFI authors. The Foreign library offers readers like readNumber, but deciding when to validate versus trust at the boundary remains a developer responsibility rather than a compiler-enforced rule.
For a project targeting both Node.js and a browser bundler, what is the safest way to verify that a PureScript module's generated exports match the expected runtime representation across compiler versions? Should validation live inside the PureScript FFI wrapper using Foreign readers, or at the JavaScript import site, and how do changing module-format defaults affect that choice?
Validate at both boundaries, but for different reasons. Use Foreign readers inside PureScript wrappers to coerce values coming from JavaScript. Assert the shape of generated exports at the JavaScript import site because the compiler can change how records, constructors, and newtypes are emitted. Module format (CommonJS versus ES modules) is a separate resolution concern; pin it explicitly instead of relying on compiler defaults.
Confirmed: foreign import erases PureScript types, so records, ADT constructors, and newtypes appear at runtime as plain objects, arrays, or functions. The Foreign library provides readers such as readNumber, readString, and readObject. Module format determines whether Node.js and bundlers resolve output with require or import, and defaults have shifted across PureScript releases.
Likely explanation: Surprising FFI layouts usually come from newtype erasure and recursive-type code generation, not from the module format itself. A module-format mismatch typically surfaces as a resolution or interop error—for example, require is not defined or an unexpected default export—rather than as a type error.
Foreign readers to every host value and turn read failures into explicit errors rather than letting invalid values propagate.It does not change whether to validate values; it changes which failure appears first. If the build switches from CommonJS to ES modules, an old JavaScript import site may break on resolution before any FFI value is inspected. Fix the module format and import interop first, then apply Foreign validation and JavaScript shape assertions.
Are you seeing module-resolution errors such as require is not defined or unexpected default exports, or are values arriving with an unexpected runtime shape? That detail determines whether to fix module configuration or add Foreign validation first.
Assumptions: standard PureScript JavaScript code generation and the Foreign FFI library. Alternative codegen backends or FFI conventions may behave differently. Exact compiler flags and default module formats vary by release; verify against the installed version.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.