Hello everyone! ![]()
A few weeks ago, I introduced phpurs (here), an alternate backend to compile PureScript into modern PHP.
Today, I’d like to announce that I’m taking all these hard-won lessons and applying them to a new project: gopurs, a modern PureScript-to-Go compiler. Still an experimental WIP, but the first official tests are already starting to show green and positive results. ![]()
Some of you might immediately (and legitimately) think: “Wait, doesn’t purescript-native (here) already do this?”. And yes, it does!
However, reading through the discussions and challenges raised by users in its thread (like initialization orders, performance limits of interface{} or module qualifications), I realized that the ecosystem has evolved drastically over the last few years. This unlocked new architectural paradigms that make (IMHO) building a completely new Go backend highly relevant today, specifically to address these exact limitations:
-
The optimizer & bootstrapping

Whilepurescript-nativewas written in Haskell and parsed rawCoreFn,gopursis written 100% in PureScript. It plugs directly into thepurescript-backend-optimizer(which I also used forphpurs). This means we do not reinvent the wheel for classical optimizations. Thegopurscompiler can then strictly focus on translating this highly-optimized AST into idiomatic, performant Go code, adding further optimizations when it can, for Go (specifically). And it remains fully accessible to anyone in the PureScript ecosystem (installable viaspagoandnpm). -
Heap vs. Stack: a new memory layout for Go

Dynamic typing in statically typed languages like Go often relies heavily oninterface{}(orany). However, assigning primitive values to interfaces forces them to escape to the heap (Boxing), generating massive Garbage Collector pressure. Forgopurs, I ran extensive benchmarks and decided to completely ditchany. Instead, the runtime uses a universal flatValuestruct (a tagged union), inspired by V8 or LuaJIT. This ensures that dynamic operations stay mostly on the stack, when possible.
To give you an idea, on a 1 billion operations benchmark, native static Go took ~250ms, a dynamicanyapproach took ~9 seconds, and theValuestruct solution completed in ~240ms! The performance difference is staggering, completely bypassing the GC overhead in heavy iterative loops. -
Up-to-date with modern PureScript

purescript-nativehasn’t seen major activity in a while.gopursaims to be fully aligned with the current v0.15+ ecosystem (and v0.16+ soon). I am currently mirroring the standard libraries (gopurs-prelude,gopurs-effect, etc.) to provide native Go FFIs.
I want to deeply acknowledge the fantastic work done by Andy on purescript-native. It really paved the way. It is always much easier to come second and learn from the limits encountered by the pioneers. This project is a response, an extension of his work, an apology, a love letter. It simply couldn’t exist without it. All my gratitude for his initial effort. ![]()
As we all know, Go offers exceptional concurrency (goroutines fit perfectly with Aff), amazing speed, and rock-solid native binaries.
Being able to bring a mesomorphic approach to this ecosystem is a great opportunity: the solid and safe part of PureScript, with the liquid and battled-test part of Go’s runtime (and FFIs). I often use this metaphor when talking about PureScript: like our modern screens (i.e. liquid crystals), it’s a language that allows us to elevate the discussions and rise above the dialectical processes, hybridizing the strengths rather than canceling them out.
The repo may be ready for production soon, this summer
(it will still be pretty messy until then)
Love ![]()
