Gleam doesn't compile to Erlang source anymore
Gleam v1.19.0: Moving Beyond Erlang Source
Gleam continues to evolve as a type-safe, scalable language designed for both JavaScript runtimes and the Erlang virtual machine (BEAM). With the release of v1.19.0, a fundamental shift has occurred in how the language interacts with Erlang.
🚀 A New Compilation Strategy
Over the past several months, Giacomo Cavalieri has spearheaded a complete rewrite of the Erlang code generator. This isn't just a refactor; it is a total redesign of the output format.
The Technical Shift
Previously, Gleam functioned by generating Erlang source code. Now, it produces Erlang abstract forms.
What are Abstract Forms? They are an intermediate representation (IR) used internally by the Erlang compiler. Essentially, it is a tree annotated with metadata that represents Erlang syntax—the same structure typically created after the Erlang tokenizer and parser have done their work.
Because these forms use Erlang's external term format (a binary encoding), Gleam can now load generated code directly into the compiler, effectively transpiling bypassing the front-end of the Erlang compilation process.
Key Improvements
The transition to abstract forms provides several critical advantages:
- Enhanced Performance: Build times for Erlang-targeted projects have dropped significantly.
- Precise Debugging: Runtime location metadata now maps directly back to the original Gleam source rather than the intermediate Erlang source.
- Result: Line numbers in stacktraces and BEAM crash reports are now perfectly accurate.
- Tooling Potential: This metadata paves the way for full integration with debuggers like
edb(though this is not yet implemented). - Code Health: The generator was a legacy component; the new version aligns with modern internal standards and raises the overall quality of the compiler.
📊 Performance Benchmarks
To quantify these gains, the team utilized José Valim's langcompilebench.
The Test Case: The benchmark measures the time required to compile a project with the following scale:
<blockquote> <b>Note:</b> Benchmarks are often contrived. While they provide a useful starting point for research, they do not capture the full complexity of real-world projects where feature sets vary. </blockquote>Version Comparison
Comparing the previous stable release (v1.17.0) to the current release (v1.19.0):
| Version | Compilation Time | Result |
|---|---|---|
| Gleam v1.17 | Higher | |
| Gleam v1.19 | Lower |
Since Gleam employs incremental compilation, the perceived speedup during daily development is even more pronounced.
Cross-Language Comparison
When compared to other popular languages, Gleam's compilation speed is remarkably competitive:
- Fastest: Gleam (JavaScript), Go, Gleam (Erlang).
- Moderate: Erlang, Elixir.
- Slower: Java, Elm, Rust, C#, TypeScript.
🛠️ Why not compile directly to BEAM bytecode?
A logical question arises: if we are already using an intermediate representation, why not skip the Erlang compiler entirely and generate BEAM bytecode directly?
While this could theoretically allow for optimizations based on Gleam's static type information, the team decided against it for several reasons:
- VM Volatility: BEAM bytecode is not a static target. It evolves with every VM release, adding or removing functionality.
- Maintenance Burden: Gleam would need to synchronize perfectly with Erlang maintainers to ensure compatibility with every new VM version.
- Optimization Gap: Replicating decades of Erlang compiler optimizations would be a massive undertaking.
- Resource Allocation: As a project without massive corporate or academic funding, Gleam must prioritize sustainable development.
The Verdict
Compiling to abstract forms represents the "sweet spot" of cost versus benefit. It is a proven path—our "older sibling," Elixir, utilizes the same method.
If it works for Elixir, it works for Gleam!