News #9:
FFI & DX: a Wasm AST Parser ![]()
One of the pain points with FFI in alternative backends is the boilerplate (manual boxing/unboxing, runtime dependencies). With gopurs, I wanted FFI to feel 100% native for Go developers.
To achieve this, the compiler uses a Go AST parser compiled to WebAssembly (ffi_gen.wasm) that analyzes your .go FFI files on the fly. It reads your Go signatures and automatically generates the perfect, highly-optimized bridging code.
1. Uncurrying & idiomatic native types
I also wanted to avoid the hassle of currying. In modern JS (ES6+), writing a => b => a + b is highly ergonomic. But in Go, manual currying quickly becomes a nightmare of nested func(interface{}) interface{} closures.
By analyzing your signatures, the Wasm parser allows you to write perfectly flat and strongly typed Go functions:
Before (Typical FFI boilerplate with manual currying & boxing):
func DoMath(arg0 gopurs_runtime.Value) gopurs_runtime.Value {
return gopurs_runtime.Func(func(arg1 gopurs_runtime.Value) gopurs_runtime.Value {
a := gopurs_runtime.Unbox[int64](arg0)
b := gopurs_runtime.Unbox[int64](arg1)
return gopurs_runtime.Box(a + b)
})
}
After (gopurs FFI):
func DoMath(a int64, b int64) interface{} {
return a + b
}
You just write pure Go. The generated bridge takes care of all the uncurrying and type conversions under the hood.
2. Flexible Effect semantics
In PureScript, Effect delays execution. Thanks to the Wasm parser, you can choose exactly how you want your native Go effects to behave, and the compiler will adapt the generated wrapper automatically:
Option A: Flattened (N+1 argument)
Perfect for simple, direct side-effects.
func Log(message string, _ interface{}) interface{} {
fmt.Println(message)
return nil
}
Option B: Curried (returning a closure)
Perfect for caching expensive pure work before the effect runs.
func MatchRegex(pattern string) func(interface{}) interface{} {
compiled := regexp.MustCompile(pattern) // Evaluated ONCE!
return func(_ interface{}) interface{} {
return compiled.MatchString("...") // Evaluated on Effect run
}
}
The Wasm parser detects your Go function’s return type (func(...)) and automatically adapts the backend optimizer to preserve your partial application caching, without sacrificing the overall uncurrying performance.
Best of both worlds.

