News #19:
The project is now officially entering its cleanup and maintainability phase.
However, as I started diving deep into the compiler’s performance and architecture, I’ve been maturing a rather radical choice: rewriting the compiler backend in Rust (a temporary repo will evolve here). I’ve managed to completely avoid RAM overloads using a few tricks (.purmeta, etc.), but the further I go, the more I realize that there are things more important than that missing (see the list below).
Here is why I’m considering this pivot:
- True parallelism: I want to fully leverage multi-core processing to parse and transform TAST files, etc. While we could theoretically bootstrap
gopursto compile itself and use Go’s concurrency, debugging a self-hosted AOT compiler that relies on its own generated code is a chicken-and-egg challenge I don’t really have the time to deal with, these days. Using a dedicated systems language like Rust gives us a direct and fearless parallelism out of the box. - Raw speed: I want the compilation times to be as fast as possible, even during single-threaded execution. E.g., Rust’s performance profile and insanely fast JSON deserialization are perfectly suited for crunching the
tcorefnpayloads. - Memory reliability: TAST never erases types, the AST graph we hold is really big. In GC-based environments, allocating millions of tiny AST nodes puts great pressure on the Garbage Collector. Rust allows us to bypass this entirely (e.g., using Arena Allocators), giving us a perfectly predictable, highly reliable, and performant memory model.
- Natural translation: you might wonder if porting from PureScript to Rust means losing our expressive FP abstractions. Thankfully, the core of this compiler isn’t heavily reliant on advanced type-level programming. It is essentially a pure pipeline transforming massive ADTs. This maps flawlessly to Rust: it won’t require translating into complex traits or fighting the lack of HKTs. It just means writing a lot of enums and exhaustive match statements. I’m oversimplifying a bit, but it guarantees a smooth architectural transition.
I want to be transparent: this decision might delay the official release by at least a month. But I believe the long-term benefits for our ecosystem are well worth the wait.
Perhaps purust will reach a level of maturity where we can leverage it directly one day to write compilers with native parallelism. But as it stands, writing the tooling directly in Rust seems to be the most reasonable path to ensure the compiler is as performant as possible.
Note: a Purescript developer could certainly use Node.js for local development (no AOT compiling penalty), and Go or Rust for production, but my initial goal was a bit more ambitious than that: I’d like to enable a Purescript developer (who doesn’t work on frontend projects) to completely forget about Node.js and npm. Not out of any particular animosity toward this technology, but for the sake of convenience (only one runtime target, no prod surprise, etc.).
Note #2: purbo has been created, and it is a direct Rust port of @natefaubion’s brilliant purescript-backend-optimizer. All credit goes to him for the semantic architecture, this will just be a translation to leverage Rust’s parallelism and memory management. That is something gopurs will rely on, like other backends in the future.
Edit: A slightly more nuanced compromise was made. More details here.






