Published 18 Dec, 2023 by Louis Pilfold
Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v0.33.0 has been published, a release that contains some features we in the community have been looking forward to for a long time! Let’s take a look.
Exhaustive exhaustiveness checking
Gleam is designed to make writing and running production software less stressful. One way it does this is by preventing runtime errors that would have you scrambling to fix bugs in the middle of the night. If you don’t use assertions then Gleam code will never throw an exception, outside of hardware problems such as memory exhaustion.
Or that was the idea. There was one place where Gleam was not living up to this promise: pattern matching.
Pattern matching with a case expression is how flow control is done in Gleam. A series of clauses are written, and the first clause with a pattern that matches the input gets run. It’s similar to a switch statement in other languages, but with some extra functionality.
pub fn describe(list: List(t)) -> String {
case list {
[] -> "Empty list"
[_] -> "Singleton list"
[_, _] -> "Two element list"
_ -> "Long list"
}
}
If no pattern matches the input then a runtime error is thrown. This is not what we want! To prevent this the Gleam compiler implements exhaustiveness checking. If there are missing patterns then it will emit an error at compile time, prompting the programmer to fix the code.
Unfortunately the implementation of exhaustiveness checking was, perhaps amusingly, inexhaustive. It did not work for more complex patterns.
With this release Gleam implements the algorithm described in the paper How to compile pattern matching by Jules Jacobs, with help from Yorick Peterse and his Pattern matching in Rust project, and as such it will now catch all missing patterns. Thank you Jules and Yorick!
Prettier printing
Gleam has a code formatter that will reformat your code to the canonical Gleam style, saving the keypresses you’d spend formatting your code, and freeing you from debates about exactly where to put those curly braces.
The formatter implements the algorithm described in the paper Strictly Pretty by Christian Lindig. We’ve been very happy with it, but there was a few places in which the format we supported was not exactly what we wanted.
let discounts =
list.map(
checkout_line_items,
fn(item) { calculate_discount(item, discount_codes) },
)
With this release the formatter has gained the “next break fits” extension found in Elixir’s pretty printer, which we now use to format the above code like so:
let discounts =
list.map(checkout_line_items, fn(item) {
calculate_discount(item, discount_codes)
})
This new format is especially impactful for code with sophisticated DSLs, such as the web application framework Lustre.
//
// Before
//
html.div(
[],
[
stack.of(
html.nav,
[stack.tight()],
[
html.h3([], [text("Guides")]),
html.a(
[href("/guides/wisp")],
[text("Integrating Lustre with Wisp framework")],
),
]
),
],
)
//
// After
//
html.div([], [
stack.of(html.nav, [stack.tight()], [
html.h3([], [text("Guides")]),
html.a([href("/guides/wisp")], [
text("Integrating Lustre with Wisp"),
]),
]),
])
Thank you to Giacomo Cavalieri for this feature!
Additionally, the gleam format
command will ignore build directories even
outside of git repositories. Thank you Alessandro Scandone.
Encouraging semantic versioning
Gleam’s package manager uses semantic versioning and the Pubgrub algorithm to pick versions of each package that are compatible with each other. This helps avoid dependency headaches, but it only works if package authors follow semantic versioning.
Developers often have a habit of using 0.x.x
versions instead of following
semantic versioning, undermining the work the package manager does to avoid
unexpected breakages. If you’re publishing a package then it’s worth taking
versioning seriously to give your users the best experience, even if you intend
to do more work on the package in future.
To encourage moving past version zero Gleam now creates new projects at 1.0.0
,
and asks for confirmation when publishing a version zero package. It will also
ask for confirmation if you try to publish a package with the name prefix
gleam_
, which implies the package is maintained by the Gleam core team.
In future Gleam may have additional publishing features, such as analysing the difference between the last published version and the current version to suggest an appropriate new semantic version number, as the Elm package manager does.
Web Assembly API revamp
The Gleam compiler can be compiled to WASM, allowing it to be used in the browser or anywhere else WASM can be used. The JavaScript bindings to this API have been improved, given support for multiple projects, and now expose warnings as well as errors.
const compiler = await import("/compiler/gleam_wasm.js");
await compiler.default();
compiler.initialise_panic_hook();
compiler.write_module(0, "mod", "pub fn add(a, b) { a + b }");
compiler.compile_package(0, "javascript")
compiler.read_compiled_javascript(0, "mod")
// This returns:
// "export function add(a, b) {\n return a + b;\n }\n"
This new API is still a little low-level. In future we may have a more sophisticated one, if that would be useful to folks. Currently this API is being used to create a new in-browser interactive playground and tutorial for Gleam, which we hope to release in January.
Language additions
The \u{...}
syntax can now be used in strings to specify unicode codepoints by
their hexadecimal number. Thank you F. Schwalbe
and Adi Salimgereev.
The panic as "..."
and todo as "..."
syntaxes now accept any expression that
evaluates to a string, rather than just a string literal. Thank you
Om Prakaash.
The words auto
, delegate
, derive
, else
, implement
, macro
, and test
are now reserved for future use. This is done as we soon to hope to release a
version 1.0.0
of Gleam, after which point we will be committed to not breaking
backwards compatibility. Reserving these words now will allow us to use them for
new language features in the future.
The bool negation !
operator can now be used in clause guard expressions.
The deprecated bit string, ambiguous type import, and JavaScript prelude inspect features have been removed.
Build tool additions
The gleam export erlang-shipment
command, used to create an artefact suitable
for deployment on a server, now supports Windows. Thank you
bun.
It is possible for Elixir and Erlang packages to have a different name according to the virtual machine than to the package manager. These packages are now supported by the build tool and can be added as dependencies to Gleam projects.
The search bar in generated HTML documentation has been improved, making better suggestions for partial matches. Thank you F. Schwalbe.
The gleam run
and gleam test
commands gain the -t flag, which is an alias of
the –target flag. Thank you shayan javani.
The short forms of js
and erl
can now be used as values for the --target
flag on all commands that accept it. Thank you
Mani Sundararajan.
Thanks
Gleam is made possible by the support of all the kind people and companies who have very generously sponsored or contributed to the project. Thank you all!
If you like Gleam consider sponsoring or asking your employer to sponsor Gleam development. I work full time on Gleam and your kind sponsorship is how I pay my bills!
- Aaron Gunderson
- Adam Brodzinski
- Adam Mokan
- Adi Iyengar
- Adi Salimgereev
- Akiomi Kamakura
- Alembic
- Alessandro Scandone
- Alex Manning
- Alex Rothuis
- Alexander Koutmos
- Alexander Stensrud
- Alexandre Del Vecchio
- Andy Aylward
- Anthony Khong
- Anthony Scotti
- Arnaud Berthomier
- Barry Moore
- Bartek Iwańczuk
- Ben Marx
- Ben Myles
- Benjamin Peinhardt
- brettkolodny
- Brian Glusman
- Bruno Michel
- bun
- Carlo Gilmar
- Carlos Saltos
- Chew Choon Keat
- Chris Lloyd
- Chris Ohk
- Chris Rybicki
- Christian Wesselhoeft
- Christopher Keele
- clangley
- Clay
- Coder
- Cole Lawrence
- Colin
- Cristine Guadelupe
- Damir Vandic
- Dan Dresselhaus
- Daniel Sherlock
- Danielle Maywood
- Danny Martini
- Dave Lucia
- David Bernheisel
- David Flanagan
- David Sancho
- Dmitry Poroh
- Edon Gashi
- Elliott Pogue
- Emma
- Erik Terpstra
- Erika Rowland
- Ernesto Malave
- Fernando Farias
- Filip Figiel
- Florian Kraft
- fly.io
- fortunewang
- Frederick Schwalbe
- Giacomo Cavalieri
- Graeme Coupar
- Guilherme de Maio
- Gustavo Villa
- Harry Bairstow
- Hayleigh Thompson
- Hazel Bachrach
- Henry Warren
- Hex
- human154
- Ian González
- inoas
- Ivar Vong
- James Birtles
- James MacAulay
- Jan Skriver Sørensen
- Jari Aarniala
- Jen Stehlik
- jiangplus
- Jimpjorps™
- Joey Kilpatrick
- John Björk
- John Gallagher
- John Palgut
- Jonas Hedman Engström
- Josef Richter
- Julian Schurhammer
- Kayla Washburn
- Kieran Gill
- Krzysztof Zbudniewek
- Kunal Kundu
- Lars Wikman
- Leon Qadirie
- lidashuang
- Mani Sundararajan
- Manuel Rubio
- Marius Kalvø
- Mark Holmes
- Mark Markaryan
- Markus
- Markus Pettersson
- Martin Janiczek
- Matt Savoia
- Matt Van Horn
- matt-savvy
- Matthias Benkort
- max-tern
- Michael Clayton
- Michael Davis
- Michael Duffy
- Michael Jones
- Michał Łępicki
- Mike Roach
- Natanael Sirqueira
- Nathaniel Knight
- NFIBrokerage
- Nick Reynolds
- Nicklas Sindlev Andersen
- NineFX
- Noah Betzen
- Ocean Armstrong Lewis
- OldhamMade
- Ole Michaelis
- Om Prakaash
- Parker Selbert
- Paul Gideon Dann
- Pete Jodo
- Philip Giuliani
- Prashant Singh Pawar
- Praveen Perera
- qingliangcn
- Raúl Chouza
- Redmar Kerkhoff
- Rico Leuthold
- Robert Attard
- Robert Ellen
- Sam Aaron
- Sammy Isseyegh
- Saša Jurić
- Scott Wey
- Sean Jensen-Grey
- Sebastian Porto
- SEKUN
- Seve Salazar
- shayan javani
- Shuqian Hon
- Signal Insights
- Simone Vittori
- Szymon Wygnański
- Terje Bakken
- Theo Harris
- Timo Sulg
- Tomasz Kowal
- Tristan de Cacqueray
- Tristan Sloughter
- tynanbe
- Weizheng Liu
- Willyboar
- Wilson Silva
- Wojtek Mach
- Xetera
- Yasuo Higano
- Yu Matsuzawa
- Zsombor Gasparin
- Šárka Slavětínská
Thanks for reading! Happy hacking! 💜