{"slug": "gleam-doesn-t-compile-to-erlang-source-anymore", "title": "Gleam doesn't compile to Erlang source anymore", "summary": "Gleam v1.19.0 was released with a rewritten Erlang code generator by Giacomo Cavalieri that outputs Erlang abstract forms instead of Erlang source code, letting the compiler load generated code directly and skip the front half of the Erlang compiler. The change improves compiler performance and build times for Gleam projects on Erlang and makes runtime location metadata accurate to original Gleam source, so line numbers in BEAM crash reports and stacktraces are precise. The rewrite's first stage shipped in v1.18.0, and the project's langcompilebench comparison of v1.17.0 to v1.19.0 shows a considerable improvement on a full build from scratch.", "body_md": "Gleam is a type-safe and scalable language for the Erlang virtual machine and\nJavaScript runtimes. Today Gleam [v1.19.0](https://github.com/gleam-lang/gleam/releases/tag/v1.19.0) has been published, so\nlet's go over what's new.\n\n## A new compilation target\n\nOver the last few months [Giacomo Cavalieri](https://github.com/giacomocavalieri)\nhas entirely rewritten Gleam's Erlang code generator that has an entirely\ndifferent design, and most notably, outputs a different format. Previously Gleam\ngenerated Erlang source code, now it generates [*Erlang abstract forms*](https://www.erlang.org/doc/apps/erts/absform.html).\n\n\"Erlang abstract forms\" is an intermediate representation used by the Erlang\ncompiler. It is a metadata-annotated tree that represents Erlang syntax, and it\nis normally produced by running Erlang's tokeniser and parser. It has a binary\nencoding using Erlang's [*external term format*](https://www.erlang.org/doc/apps/erts/erl_ext_dist.html),\nand with this binary format we can load our generated code directly, skipping\nthe front-half of the Erlang compiler.\n\nThis new Erlang code generator brings several benefits:\n\n- \nThe performance of the compiler has been improved, significantly reducing the build times for Gleam projects running on Erlang.\n- \nThe location metadata available to the runtime is now accurate to original Gleam source code, rather than to the Erlang source code the compiler would generate. This means, for example, the line numbers in BEAM crash reports and stacktraces are perfectly accurate, while previously they could be inaccurate, only pointing to the nearest function. This metadata could also enable full support for Gleam in debuggers such as [edb](https://github.com/WhatsApp/edb) ,\nthough we have not done any work on this ourselves.\n- \nThe code-quality of the Gleam compiler has been improved. The Erlang code generator was one of the oldest and most stable parts of the Gleam codebase, so while it wasn't causing us any problems it wasn't conforming to the standards and conventions we have today. This new replacement is excellent, and arguably raising the bar for the compiler as whole.\n- \nWe never have to hear someone use the word \"transpiler\" as a pejorative ever again. [<sup>1</sup>](#fn1)\n\n## Ok, so how fast is it?\n\nI'm going to show you some numbers in a moment, but please remember that benchmarks are always contrived and never tell the full story. This data could be a good introduction or jumping-off point, but a good understanding requires the person to do further research and experience.\n\nThe [benchmark](https://github.com/lpil/langcompilebench) is based on José\nValim's `langcompilebench` project. Thank you José! It is a measure of the time\ntaken to compile 100 modules that each contain 100 functions that return a\n\"hello world\" string. This is practical as this shape of test project can be\neasily replicated across different languages to produce the most like-for-like\ntest projects, but it is limited in what it can tell us as only a small subset\nof the features of each language get compiled. In real projects code will be\ngreatly more varied, and different features will have different compilation\ncosts in different languages.\n\nThe first stage of the code generator rewrite was released in v1.18.0, the previous release, so let's compare v1.17.0 to the newly released v1.19.0. This chart shows the time taken to compile the benchmark project, lower is better.\n\nAs you can see, a considerable improvement! This is a full build from scratch, without any caching. Gleam's compilation is incremental, so during typical development it would be much faster as it will not be compiling the entire project.\n\nThe original `langcompilebench` includes only Erlang, Elixir, and Gleam, but I\nhave extended it an assortment of other popular programming languages, to help\nfolks get a rough feel for how fast Gleam compiles compared to a language they\nare familiar with. I've also included Gleam when compiling to JavaScript.\nHere's the results:\n\nRemember: This is a contrived benchmark and is this alone is insufficient to draw any hard conclusions about these languages. That said, these results do suggest that Gleam's compilation is nice and fast, and as a Gleam programmer I can say that Gleam development is very enjoyable, with little time spent waiting for the computer.\n\n## Why not target BEAM bytecode directly?\n\nWe have moved from from compiling from source code that is fed to the Erlang compiler to an intermediate-representation that is fed into the Erlang compiler, but why not bypass the Erlang compiler altogether? Couldn't we make a BEAM bytecode generator that outperforms the Erlang compiler? Perhaps we could also use Gleam's type information to generate more optimised code too.\n\nWhile it is possible to achieve these benefits, it's unlikely we would be able to. Unlike Erlang source and Erlang abstract forms, BEAM bytecode is not fixed and unchanging. Each new release of the virtual machine can evolve and improve the bytecode, adding new functionality and sometimes removing functionality that has been made redundant. We would need to commit to forever keeping up-to-date with this evolution, working closely with the Erlang maintainers to be ready for up-coming changes, and to have new versions of Gleam ready for new releases of the virtual machine. It would also be a significant effort to reproduce all the existing optimisations that have been implemented in the Erlang compiler over the decades, even with the help we might have from Gleam's more capable static analysis.\n\nGleam is a community project supported by [sponsorship](https://gleam.run/sponsor/).\nWe have only a fraction of the finances of languages that are\nbacked by corporations or academic institutions, so we need to think carefully\nabout the most efficient and sustainable ways to use our resources.\nGleam is a reliable foundation for software development, every decision we make\nhas to work for years and decades to come. Compiling to Erlang abstract forms\nis the cost-benefit sweet-spot for Gleam today.\n\nWe're also in great company with this decision. Our much-loved older-sibling language Elixir also compiles to Erlang via abstract forms. If it's good enough for Elixir, then it's good enough for Gleam!\n\nAlright, enough about that. There's plenty more in Gleam v1.19.0 to go-over.\n\n## JavaScript decision tree assignment optimisation\n\nIt's not just the Erlang code generation that has seen some love, there's some good improvements for JavaScript too.\n\nIn Gleam flow control is done with pattern matching using a `case` expression,\nand it gets compiled to nested `if` statements. Because pattern matching is\ndeclarative the compiler is able to reorder and optimise the runtime logic,\nusing a divide-and-conquer approach to find the right branch as quickly as\npossible.\n\n[John Downey](https://github.com/jtdowney) has improved this process to\ngenerate flatter code, with nested `if` statements collapsed into a single\ncondition and fewer intermediate variables. For example, take this Gleam code:\n\n``` php\npub fn go(x) {\n  case x {\n    Wibble(1, 2) -> 1\n    _ -> 2\n  }\n}\n```\n\nPreviously this small bit of Gleam would compile to this surprisingly large bit\nof JavaScript[<sup>2</sup>](#fn2):\n\n```\nexport function go(x) {\n  if (isWibble(x)) {\n    let $ = x[0];\n    if ($ === 1) {\n      let $1 = x[1];\n      if ($1 === 2) {\n        return 1;\n      } else {\n        return 2;\n      }\n    } else {\n      return 2;\n    }\n  } else {\n    return 2;\n  }\n}\n```\n\nBut now it generates this:\n\n```\nexport function go(x) {\n  if (isWibble(x) && x[0] === 1 && x[1] === 2) {\n    return 1;\n  } else {\n    return 2;\n  }\n}\n```\n\nA nice improvement, I'm sure you'll agree! Surprisingly this makes little-to-no change to the size of code bundles once minified and compressed (gzip really is magic), but the resulting code has fewer branches for JavaScript engines to optimise.\n\nThank you John!\n\n## List literal optimisation\n\nWhile they share a syntax in their respective languages, Gleam's immutable persistent list type is not the same as the JavaScript mutable contiguous array type. When Gleam code is compiled to JavaScript any list literal has to be compiled to JavaScript code that constructs the runtime data structures. For example, take this Gleam code:\n\n``` js\nlet numbers = [1, 2, 3]\n```\n\nThis would compile to JavaScript code[<sup>2</sup>](#fn2) like this, where a JavaScript array is\nconstructed and passed to a function to convert it to a Gleam list.\n\n``` js\nconst numbers = arrayToList([1, 2, 3])\n```\n\nWith this release the compile will now generate different code for short list literals, generating more direct code that does not convert from an array.\n\n``` js\nconst numbers = prepend(1, prepend(2, prepend(3, empty)))\n```\n\nWith modern JavaScript engines this results in a nice performance improvement,\nand it is especially impactful for projects that use lots of short lists, like\nthose using the [Lustre](https://lustre.hexdocs.pm) library. We recorded no\nimprovement for longer lists, so the array-to-list approach is still used for\nthose.\n\nThank you [Giacomo Cavalieri](https://github.com/giacomocavalieri) for this!\n\n## TypeScript API overloads\n\nWhen compiling to JavaScript the Gleam compiler will also generate\n[functions](https://gleam.run/documentation/externals/#Gleam-data-in-JavaScript)\nfor working with the programmer-defined data structures from JavaScript.\nAlongside this the compiler can also provide TypeScript declaration files,\nenabling full integration between TypeScript and Gleam in a single project.\n\nOne of the functions provided for each custom type is a function to check whether a value is a particular variant or not. For example, given the following type:\n\n```\npub type Box(value) {\n  Full(value)\n  Empty\n}\n```\n\nThe generated function would have this TypeScript declaration:\n\n```\nexport function Box$isFull(value: any): value is Full<unknown>;\n```\n\nThe keen-eyed TypeScript programmer readers may notice a problem here. If the\nvalue is already known to be of type `Box<number>`, then this function can be\nused to refine the container type to `Full`, but the type parameter of `number`\nis generalised to `unknown`, causing type information loss. This is very\ncumbersome.\n\n[Giacomo Cavalieri](https://github.com/giacomocavalieri) has added an overload\nto the definition, so the type is preserved whenever possible.\n\n```\nexport function Box$isFull<I>(value: Box$<I>): value is Full<I>;\nexport function Box$isFull(value: any): value is Full<unknown>;\n```\n\nThank you Giacomo!!\n\n## Improvements for other build tools\n\nGleam users will typically use the official build tool that is part of the\n`gleam` executable, but sometimes folks will want to compile and use Gleam code\nin other contexts. For example, an Elixir or Erlang programmer may want to use\na dependency package that is written in Gleam. This works fantastically at runtime\nas these three BEAM languages have excellent zero-cost interop, but getting to\nthis point can be tricky, as Elixir and Erlang's main build tools do not have\nbuilt-in support for Gleam.\n\nThe `gleam` executable offers several commands that expose compiler\nfunctionality, for use by other build tools. This release includes several\nimprovements to these commands, with the intent of getting Gleam support in\nElixir's Mix and Erlang's rebar3.\n\nThe Erlang virtual machine requires all packages too have a `.app` resource\nfile along with the compiler bytecode. Previously these Gleam-supporting build\ntools would be expected to provide these, but now `gleam` will generate the\nfiles for them when compiling to BEAM.\n\nThe `compile-package` command gains a `--no-dev` flag, which will have the\ncompiler only load code from the `src` directory and to skip packages listed as\n`dev_dependencies`.\n\nThe `export package-information` and `export package-interface` commands can\nnow print their information to stdout, while previously they would have to\nwrite to a file. Alongside that, the `export javascript-prelude` and ```\nexport\ntypescript-prelude\n```\n commands can now write to a file.\n\nThank you [Rodrigo Álvarez](https://github.com/Papipo) for these additions!\nHopefully we will see Gleam support in Elixir's Mix build tool soon.\n\n## Language server label support\n\nGleam has an excellent language server built-in, providing IDE functionality to\nall editors that support the language server protocol. Possibly the last piece\nof major functionality was full support for labels of fields and arguments.\n[Alistair Smith](https://github.com/alii) has fixed this, adding support for\ngo-to-definition, find-references and renaming of labels! Thank you Alistair, I\nknow many people will be absolutely delighted by this time-saving feature.\n\n## Formatting Gleam code in the browser\n\nThere is a WebAssembly build of the compiler, used by [the language\ntour](https://tour.gleam.run) and [the\nplayground](https://playground.gleam.run) to compile Gleam in the web browser. \n[John Downey](https://github.com/jtdowney) has added a new `format_source`\nfunction, enabling people to run the Gleam code formatter inside the browser.\nWe will add this functionality to the playground in the near future. Thank you\nJohn!\n\n## As always, better error messages\n\nMaking error messages as clear and as helpful as possible is very important to us. It's all very well for a tool to be nice to use when things are going well, it is when things are going badly that the experience can really help or hurt the programmer's stress levels.\n\nSmall accidental syntax errors can be a pain, especially if you're not sure what and where the mistake is.\n\n[0xda157](https://github.com/0xda157) has added a special error for when a git\nmerge conflict marker is found in the code, and another for when the\n`User(..lucy, score: 10)` record update syntax is written with the original\nrecord in the wrong position like `User(score: 10, ..lucy)`. She has also added\nan errors for procedural operators that do not exist in Gleam, such as `+=` and\n`*=`.\n\n[n0kk23](https://github.com/n0kk23) has added a custom helpful error message\nfor when `|` is used in pattern matching in a way that is not valid syntax in\nGleam, but is valid in other languages, such as Java.\n\n[Giacomo Cavalieri](https://github.com/giacomocavalieri) has added a helpful\nerror message for binary operators that are not permitted in constant\nexpressions, and at the same time he has improved the fault\ntolerance[<sup>3</sup>](#fn3) of the compiler in the presence of these mistakes.\n\n[Andrey Kozhev](https://github.com/ankddev) has added extra context to the\nerror message for when a module tries to use a private type or value from\nanother module within the same package, letting them know that while it does\nexist, it is private. We do not offer this for modules from dependency\npackages, to avoid leaking information about code the programmer does not\nmaintain.\n\nAnd lastly, [James Dolan](https://github.com/jamesdolan16) has improved the\ntype checker such that an invalid type alias definition can no longer cause a\ncascade of further errors through all usages of the alias.\n\nThank you all for making Gleam debugging easier and easier.\n\n## And the rest\n\nAnd thank you to the bug fixers and experience polishers:\n\n[0xda157](https://github.com/0xda157),\n[Amr Kadry](https://github.com/Amrkadry),\n[Andrey Kozhev](https://github.com/ankddev),\n[Giacomo Cavalieri](https://github.com/giacomocavalieri),\n[Hari Mohan](https://github.com/seafoamteal),\n[Ian Chamberlain](https://github.com/ian-h-chamberlain),\n[Jack Programs](https://github.com/jackprogramsjp),\n[John Downey](https://github.com/jtdowney),\n[Lillian Rose](https://github.com/lillianrubyrose),\n[Mar Bloeiman](https://github.com/strawmelonjuice),\n[mmustafasenoglu](https://github.com/mmustafasenoglu),\n[Naomi Roberts](https://github.com/naomieow),\n[Rodrigo Álvarez](https://github.com/Papipo),\n[Senthilnathan](https://github.com/ssenthilnathan3),\n[Surya Rose](https://github.com/GearsDatapacks), and\n[Vivid](https://github.com/absolutely-vivid).\n\nFor full details of the many fixes and improvements they've implemented see [the\nchangelog](https://github.com/gleam-lang/gleam/blob/main/changelog/v1.19.md).\n\n## A call for support\n\nGleam is not owned by a corporation; instead it is entirely supported by sponsors, most of which contribute between $5 and $20 USD per month, and Gleam is my sole source of income.\n\nWe have made great progress towards our goal of being able to appropriately pay\nthe core team members, but we still have further to go. Please consider\nsupporting [the project or core team members](https://gleam.run/sponsor/).\n\nThank you to all our sponsors! And special thanks to our top sponsor:\n\n1. \n\"Transpiler\" means a compiler that outputs a human-readable format, such as source code. It's a cool sounding word, but most the time people use it to imply that a given compiler is in some way inferior. This is very silly, as there is nothing about compiling to a human-readable format that makes a compiler easier to implement. If you care about that output being nicely formatted it might even be harder than using a binary format. [↩︎](#fnref1)\n2. \nThe code has been slightly edited for clarity, but the parts related to this improvement are unchanged. [↩︎](#fnref2)\n3. \nGleam's compiler is the heart of the Gleam language server, so unlike traditional compilers it needs to be able to provide information about code even when it is in invalid state. If only valid code could be fully analysed then the language server would provide a degraded experience to the programmer when they are half-way-through a refactoring or other large edit. [↩︎](#fnref3)", "url": "https://wpnews.pro/news/gleam-doesn-t-compile-to-erlang-source-anymore", "canonical_source": "https://gleam.run/news/gleam-doesnt-compile-to-erlang-source-anymore/", "published_at": "2026-10-05 17:29:17+00:00", "updated_at": "2026-10-05 18:17:49.843274+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Gleam", "Giacomo Cavalieri", "Erlang", "JavaScript", "Erlang abstract forms", "BEAM", "edb", "langcompilebench"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/gleam-doesn-t-compile-to-erlang-source-anymore", "markdown": "https://wpnews.pro/news/gleam-doesn-t-compile-to-erlang-source-anymore.md", "text": "https://wpnews.pro/news/gleam-doesn-t-compile-to-erlang-source-anymore.txt", "jsonld": "https://wpnews.pro/news/gleam-doesn-t-compile-to-erlang-source-anymore.jsonld"}}