What is the relationship between Node and PureScript?

Just starting out with PureScript, and used easy-purescript-nix to set it up, but when I tried out an example from Jordan’s reference on the REPL, it failed with a reference to Node js:

> data Unit = Unit
> unit = Unit
> 
> :paste
… type C val = (Unit -> val)
… 
… d :: C Int
… d neverUsed = 3
… 
> d unit
Could not find node.js. Do you have node.js installed and
available in your PATH?

After Node was installed, it started working so no issues there. I am wondering though how tightly coupled is PureScript with Node?

I was under the impression that Spago takes care of the PureScript dependencies, while you continue to use npm to manage node/JS dependencies so one can just switch to another backend (such as deno) when they want to.

(Also not an expert with Node, and I know that deno doesn’t have a package manager so that would probably complicate things. That is, it would for me for now.)


Edit: Or is it only the REPL (PSCi?) that needs Node to work? Just found purescript/documentation issue #109 so a Node-less workflow is definitely possible.

2 Likes

The principal implementation of Purescript is using Javascript as a compiling target. Thereby Node.js is an one available runtime for Purescript. A bit like how C# compiles to CLR.

Use of a dynamically typed language as runtime for a typed compiled language is a good design choice, and it has been done before this. A long time ago Axiom computer algebra system was implemented on top of Lisp.

This does not answer your question in full, but it will spare other people from telling this. I’d love if you get a great answer. I’m interested about this too.

3 Likes

The standard PureScript backend compiles to Ecma Script 3 and CommonJS modules. Ecma Script 3 is a language and runtime specification supported by all modern JavaScript engines. You can run the output of the compiler on any JavaScript runtime.

For every PureScript file/module the compiler creates a single file (you can find them in /output) in this special CommonJS module format. CommonJS requires a few global variable and functions to be available. To run the output code we have to either use a module bundler like Webpack, Rollup or Parcel to make these globals available (or compile them away) and create a single JavaScript file. Or we can use Node.js, which supports the CommonJS module format by default, to execute the code. Node.js does not bundle the files and can directly work with the results of the output directory. I should also mention that these bundlers are usually written in JS, even though there seem to be new competitors coming up to massively improve performance.

You can also use other backends of the PureScript compiler. Then PureScript will produce a different output language (e.g. C) that can then use the specific module/import format of this language. It is also being discussed to use ES modules as an output format of the JS backend as module bundlers and Node.js now support ES modules as well.

The PureScript compiler does not need Node.js as it is written in Haskell. But the REPL compiles your expressions into JS and evaluates them in Node.js. Not all expressions are turned into JS code, e.g. the type system is obviously not part of the output. I am not sure if the backend of the REPL can be changed or if it simply uses the one your PS compiler uses.

Another thing to consider is the FFI. If a library makes use of the FFI, the FFI code obviously needs to be written in the compiler target language. And not only that, the FFI might make use of global variables that are only available in a specific runtime. The PureScript browser libraries bridge to JS code that uses DOM functions that are only available in the browser. Similarly a http server library might make use of the http module from Node.js and can only be used inside of Node.

Now there are a few use-cases where you might not need Node.js in your toolchain to create (and run) your build. E.g. when you are targeting a non-JS backend or when you just want to produce output files in CI (or a Docker multistage build).

Deno is not a JS runtime but a TypeScript runtime. While they recently made the decision to write internal modules in JS, I think the compiler would still have to output TS. All JS should be valid TS but there might still be type errors because the TS compiler is not able to correctly infer all types. So making PureScript run on deno might not be a simple task for this reason and all the reasons mentioned above.

5 Likes

First of all, thank you for the thorough write-up!

This paragraph entirely answers my question but other info you provided already takes care of my follow-up questions I had in mind.

Yes, this is what threw me off when I encountered the REPL error. Learning PureScript to also be able to try out purerl but I know that there are many other alternative backends so if PureScript would so tightly coupled with Node, it would certainly be a pain to port it to other languages.

Thanks for mentioning this! Just found the relevant discussions:

According to their website, it is both. (Not trying to put up an argument; will have to do a lot more research.) The work currently being done on ES modules will definitely make it easier to use it with deno though.