Published 27 May, 2024 by Louis Pilfold
Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.2.0 has been published, a release that focuses on improving the language server and developer experience. It’s a big one both in terms of size and impact, so let’s take a look as what it includes.
Fault tolerant compilation
Gleam’s compiler has traditionally halted immediately when a compile time error was encountered, presenting the error to the programmer and requiring them to fix it before compilation can be attempted again.
The main advantage of this is that the programmer has just the first and most important error, rather than a mix of the real error and a bunch of red-herring cascading errors. On the other hand, if there’s multiple genuine errors the programmer can only see the first one, which may not be the one they want to work on, or the most understandable.
Even worse, this halting behaviour causes major issues for the Gleam Language Server (the engine that text editors like Neovim, Helix, Zed, and VS Code use to get IDE features for Gleam). The language server internally uses the compiler to build and analyse Gleam projects, so when the compiler halts with an error the language server is left without up-to-date information about the code. Without a successful build some language server features may not work, and the longer a project goes without successful compilation (such as during a large refactoring) the more drift there can be between the language server’s understanding of the code and the reality.
With this release when the compiler encounters an error during analysis it will move on to the next definition in the module, returning all of the errors along with updated code information once the whole module has been analysed. This has resulted in a dramatic improvement in the experience of using the Gleam language server, it being much more responsive and accurate in its feedback.
In future releases we will continue to improve the granularity of the fault tolerant compilation and we will also introduce fault tolerant parsing.
Thanks to Ameen Radwan and myself for this feature!
Language server imports improvements
This is a Gleam import statement:
import gleam/option.{type Option, Some, None}
// ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
// the module unqualified imports
The language server could previously autocomplete module names, but now it can also autocomplete types and values when importing them in the unqualified fashion.
You can also hover types and values in an import statement to see their documentation, and use go-to definition on them and the module itself to jump to where they defined in your project or its dependencies.
Single line pipelines
Gleam’s much loved |>
pipe operator can be used to pipe the output from one
function into the input of another, not dissimilar to |
pipes in bash, etc.
Gleam’s code formatter would always put each function of a sequence of pipes on
individual lines, but now you can opt to put them all on one line, so long as
altogether they are short enough to fit.
[1, 2, 3] |> list.map(int.to_string) |> string.join(with: "\n")
In addition you can also force the formatter to break a pipe on multiple lines by adding a line break. This:
[1, 2, 3] |> list.map(int.to_string)
// By putting a newline here I'm telling the formatter to split the pipeline
|> string.join(with: "\n")
Will turn into this:
[1, 2, 3]
|> list.map(int.to_string)
|> string.join(with: "\n")
Thank you to our formatter wizard Giacomo Cavalieri for this feature!
Improved error messages for use
expressions
Gleam’s use
expressions
are very powerful and can be used to replicate many built-in features of other
languages such as Go’s defer
, procedural languages’ early returns, Haskell’s
do-notation, and more. As an unusual and powerful feature it can be confusing
at first, and that was made worse as any type errors from incorrect use
expressions would be generic ones rather than being specific to use
.
These errors have been greatly refined and we hope this will help folks learning the language and debugging their code. Here’s some specific examples:
This error message is used when the right-hand-side of <-
in a use
expression is not a function:
error: Type mismatch
┌─ /src/one/two.gleam:2:8
│
2 │ use <- 123
│ ^^^
In a use expression, there should be a function on the right hand side of
`<-`, but this value has type:
Int
See: https://tour.gleam.run/advanced-features/use/
This error message is used when the right-hand-side of <-
is a function,
but it takes no arguments:
error: Incorrect arity
┌─ /src/one/two.gleam:3:8
│
3 │ use <- func
│ ^^^^ Expected no arguments, got 1
The function on the right of `<-` here takes no arguments.
But it has to take at least one argument, a callback function.
See: https://tour.gleam.run/advanced-features/use/
And this one is used when it takes arguments, but not the correct number of arguments:
error: Incorrect arity
┌─ /src/one/two.gleam:3:8
│
3 │ use <- f(1, 2)
│ ^^^^^^^ Expected 2 arguments, got 3
The function on the right of `<-` here takes 2 arguments.
All the arguments have already been supplied, so it cannot take the
`use` callback function as a final argument.
See: https://tour.gleam.run/advanced-features/use/
And more! Too many to list here. Thank you to Giacomo Cavalieri for these big improvements!
Type/value mix up feedback
One common mistake is to try and import a type as a value, or a value as a type. When an imported item is found not to exist the compiler will now check to see if there is a matching type or value and explain the mix-up.
error: Unknown module field
┌─ /src/one/two.gleam:1:19
│
1 │ import gleam/one.{One}
│ ^^^ Did you mean `type One`?
`One` is only a type, it cannot be imported as a value.
error: Unknown module type
┌─ /src/one/two.gleam:1:19
│
1 │ import gleam/two.{type Two}
│ ^^^^^^^^ Did you mean `Two`?
`Two` is only a value, it cannot be imported as a type.
Thank you Pi-Cla for this feature!
Assertion exhaustiveness checking
Gleam has a let assert
syntax to apply a pattern to a value, extracting values
contained within if the pattern matches, and crashing the program if it does
not.
The compiler now performs exhaustiveness checking on these assertions to find
patterns that are total and warn that the assert
is redundant as a result.
warning: Redundant assertion
┌─ /home/lucy/src/app/src/app.gleam:4:7
│
4 │ let assert x = get_name()
│ ^^^^^^ You can remove this
This assertion is redundant since the pattern covers all possibilities.
Thank you Giacomo Cavalieri for this feature.
Catching common crash mistakes
Gleam has todo
and panic
expressions, both of which crash the program due to
it being incomplete or in an invalid state respectively. The syntax looks like
this:
panic as "The session is no longer valid, this should never happen!"
A common mistake is to try and give the message string using the function call syntax like so:
panic("The session is no longer valid, this should never happen!")
This is valid Gleam code, but it is not using that string as a message for the panic. The string is unreachable as the program panics before it is evaluated. A compiler warning is now emitted when code like this is found.
warning: Todo used as a function
┌─ /src/warning/wrn.gleam:2:16
│
2 │ todo(1)
│ ^
`todo` is not a function and will crash before it can do anything with
this argument.
Hint: if you want to display an error message you should write
`todo as "my error message"`
See: https://tour.gleam.run/advanced-features/todo/
Thank you Giacomo Cavalieri for this feature!
Invalid constant error message improvement
Gleam’s constants are limited to a subset of the language that be evaluated at compile time, and as such arbitrary functions cannot be called within them. Previously if you tried to call a function you would get a somewhat confusing parse error, but with this version it’ll emit a much more helpful error message.
error: Syntax error
┌─ /src/parse/error.gleam:3:18
│
3 │ const wib: Int = wibble(1, "wobble")
│ ^^^^^^^ Functions can only be called within other functions
Thank you Nino Annighoefer for this improvement!
Unreachable code detection
Code that comes after a panic
expression is unreachable and will never be
evaluated as the program will crash before it is reached. The compiler will now
warn in these situations to catch these mistakes.
For example, this code will emit this warning:
pub fn main() {
panic
my_app.run()
}
warning: Unreachable code
┌─ /src/warning/wrn.gleam:3:11
│
3 │ my_app.run()
│ ^^^^^^^^^^^^
This code is unreachable because it comes after a `panic`.
Thank you Giacomo Cavalieri for this feature!
Further Hex integration
Gleam is part of the Erlang ecosystem, and as such it uses the Hex package manager.
The gleam hex revert
has been added to the Gleam binary. It can be used to
unpublish a package release within the first 24 hours of it being published.
After 24 hours Hex package releases become immutable and cannot be removed.
Additionally the error message that is emitted when you attempt to publish a package that has already been published has been improved:
error: Version already published
Version v1.0.0 has already been published.
This release has been recently published so you can replace it
or you can publish it using a different version number
Hint: Please add the --replace flag if you want to replace the release.
Thank you Pi-Cla for these features!
Erlang module collision prevention
The Erlang virtual machine supports loading new versions of existing module, enabling an application to be upgraded while it is still running. One unfortunate consequence of this is that if you accidentally reuse a module name loading it into the VM will cause the application to be upgraded to the new version, causing confusing errors as functions that no longer exist are called and crash.
The Gleam build tool would return an error and refuse to compile Gleam modules that would overwrite each other, but with this version it will also emit an error if a Gleam module would overwrite a built-in Erlang/OTP module.
error: Erlang module name collision
The module `src/code.gleam` compiles to an Erlang module named `code`.
By default Erlang includes a module with the same name so if we were to
compile and load your module it would overwrite the Erlang one, potentially
causing confusing errors and crashes.
Hint: Rename this module and try again.
Better build tool errors
A helpful error message is now shown if the manifest.toml
file has become
invalid, perhaps due to a git rebase or accidental edit.
error: Corrupt manifest.toml
The `manifest.toml` file is corrupt.
Hint: Please run `gleam update` to fix it.
Previously when no package versions could be found that satisfy the project’s requirements, a very cryptic message would printed that detailed all the constraints from all the versions that were incompatible. This was overwhelming and practically impossible to read, so it has been replaced with a much simpler message.
error: Dependency resolution failed
An error occurred while determining what dependency packages and
versions should be downloaded.
The error from the version resolver library was:
Unable to find compatible versions for the version constraints in your
gleam.toml. The conflicting packages are:
- hellogleam
- lustre_dev_tools
- glint
Thank you zahash for these improvements!
Redundant pattern matching warnings
Gleam’s case expressons support pattern matching on multiple values at the same time. This is uncommon so people used to other languages may habitually wrap multiple values in a tuple or a list to match on them. The compiler will now warn when this is done:
warning: Redundant list
┌─ /src/warning/wrn.gleam:2:14
│
2 │ case [1, 2] {
│ ^^^^^^ You can remove this list wrapper
Instead of building a list and matching on it, you can match on its
contents directly.
A case expression can take multiple subjects separated by commas like this:
case one_subject, another_subject {
_, _ -> todo
}
See: https://tour.gleam.run/flow-control/multiple-subjects/
Thank you Giacomo Cavalieri for this feature.
Redundant pattern matching auto-fix
And further still, if you are using the Gleam language server then a code action is offered to automatically fix redundant tuple wrappers. Place your cursor on the tuple and select this language action and the language server will remove the tuple from the values as well as from all patterns in the case expression.
case #(x, y) {
#(1, 2) -> 0
#(_, _) -> 1
}
Is rewritten to:
case x, y {
1, 2 -> 0
_, _ -> 1
}
Thank you Nicky Lim for this code action, and for laying the foundation for other code actions like this in future!
A helping hand for JavaScript programmers
JavaScript programmers sometimes type ===
by mistake in their Gleam code. We
have an error message for that too now:
error: Syntax error
┌─ /src/parse/error.gleam:4:37
│
4 │ [1,2,3] |> list.filter(fn (a) { a === 3 })
│ ^^^ Did you mean `==`?
Gleam uses `==` to check for equality between two values.
See: https://tour.gleam.run/basics/equality
Thank you Rabin Gaire for this feature!
And the rest
If you’d like to see all the changes for this release, including all the bug fixes, check out the changelog in the git repository.
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!
Alternatively consider using our referral link for CodeCrafters, who have recently launched a course on implementing Redis in Gleam. Use your training budget to learn and to support Gleam too!
- Aaron
- Aaron Gunderson
- Abdulrhman Alkhodiry
- ad-ops
- Adam Brodzinski
- Adam Johnston
- Adi Iyengar
- Adi Salimgereyev
- aelishRollo
- Aiden Fox Ivey
- Ajit Krishna
- Alembic
- Alex Manning
- Alexander Koutmos
- Alexander Stensrud
- Alexandre Del Vecchio
- Ameen Radwan
- AndreHogberg
- Andy Aylward
- Anthony Khong
- Anthony Maxwell
- Anthony Scotti
- Arnaud Berthomier
- Arthur Weagel
- Austin Daily
- Barry Moore
- Bartek Górny
- Ben Martin
- Ben Marx
- Ben Myles
- Benjamin Peinhardt
- Benjamin Thomas
- bgw
- Bill Nunney
- brettkolodny
- Brian Dawn
- Brian Glusman
- Bruno B.
- Bruno Michel
- bucsi
- Carlo Munguia
- Carlos Saltos
- Chad Selph
- Charlie Govea
- Chaz Watkins
- Chew Choon Keat
- Chris Donnelly
- Chris King
- Chris Lloyd
- Chris Ohk
- Chris Rybicki
- Christopher Dieringer
- Christopher Keele
- clangley
- Clay
- Cleo
- CodeCrafters
- Coder
- Cole Lawrence
- Colin
- Comamoca
- Constantine Manakov
- Cristine Guadelupe
- ctulb
- Damir Vandic
- Dan Dresselhaus
- Daniel
- Daniel Hayes
- Danielle Maywood
- Danny Arnold
- Danny Martini
- Darshak Parikh
- Dave Lucia
- David Bernheisel
- David Sancho
- Denis
- Dennis Dang
- dennistruemper
- dependabot[bot]
- Dezhi Wu
- Dillon Mulroy
- Dima Utkin
- Dmitry Poroh
- ds2600
- Edgars Burtnieks
- Edon Gashi
- Efstathiadis Dimitris
- Eileen Noonan
- eli
- Elie Labeca
- Elliott Pogue
- ellipticview
- Emma
- EMR Technical Solutions
- Erik Lilja
- Erik Terpstra
- erikareads
- ErikML
- Ernesto Malave
- Felix Mayer
- Fernando Farias
- Filip Figiel
- Fionn Langhans
- Florian Kraft
- fly.io
- Francisco-Montanez
- Georg H. Ekeberg
- Giacomo Cavalieri
- Graeme Coupar
- grotto
- Guilherme de Maio
- Guillaume Hivert
- Hamir Mahal
- Hammad Javed
- Hampus Kraft
- Hannes Schnaitter
- Hayes Hundman
- Hayleigh Thompson
- Hazel Bachrach
- Henning Dahlheim
- Henry Firth
- Henry Warren
- Hex
- human154
- Humberto Piaia
- Ian González
- Ian M. Jones
- Igor Rumiha
- inoas
- Isaac
- Isaac Harris-Holt
- Ismael Abreu
- Ivar Vong
- Iván Ovejero
- J. Rinaldi
- Jack Malcom
- Jacob Lamb
- James Birtles
- James MacAulay
- Jan Skriver Sørensen
- Jean-Luc Geering
- Jen Stehlik
- Jenkin Schibel
- Jeremy Jacob
- jiangplus
- Jimpjorps™
- Jiri Luzny
- Joey Kilpatrick
- Johan Strand
- John Björk
- John Gallagher
- John Pavlick
- Jonas Dahlbæk
- Jonas Hartmann
- Jonas Hedman Engström
- Jonas Hietala
- Josef Richter
- Joshua Steele
- Julian Schurhammer
- Kevin
- Kevin Schweikert
- Kieran Gill
- kodumbeats
- Kramer Hampton
- Kryštof Řezáč
- Krzysztof G.
- Lars Kappert
- Leandro Ostera
- Len Blum
- Leon Qadirie
- lidashuang
- LighghtEeloo
- Loïc Tosser
- Lucas Pellegrinelli
- Luci Phillips
- Lucian Petic
- Luna
- Luna Schwalbe
- mahcodes
- Manuel Rubio
- Marcel
- Marcus André
- Marcøs
- Mariano Uvalle
- Marius Kalvø
- Mark Holmes
- Mark Markaryan
- Mark Spink
- Martin Janiczek
- Martin Rechsteiner
- Mateusz Ledwoń
- Matt Champagne
- Matt Savoia
- Matt Van Horn
- Matthias Benkort
- Max Hung
- Max McDonnell
- max-tern
- Michael Duffy
- Michael Jones
- Michael Kieran O’Reilly
- Michael Kumm
- Mike
- Mike Roach
- Mikey J
- Milco Kats
- MoeDev
- Moshe Goldberg
- MystPi
- MzRyuKa
- n8n - Workflow Automation
- Nashwan Azhari
- Natanael Sirqueira
- Nathaniel Knight
- Nayuki
- NFIBrokerage
- Nick Chapman
- Nick Reynolds
- Nicklas Sindlev Andersen
- Nicky Lim
- NineFX
- Nino Annighoefer
- Nomio
- Ocean Armstrong Lewis
- OldhamMade
- Ole Michaelis
- optizio
- P. Envall
- PastMoments
- Patrick Wheeler
- Paul Gideon Dann
- Paul Guse
- Pawel Biernacki
- Pete Jodo
- Peter Rice
- Peter Saxton
- PgBiel
- Philip Giuliani
- Pi-Cla
- Piotr Szlachciak
- qingliangcn
- Qynn Schwaab
- Rabin Gaire
- Race Williams
- Rahul Butani
- Ratio PBC
- Raúl Chouza
- Redmar Kerkhoff
- Renovator
- Richard Viney
- Rico Leuthold
- Ripta Pasay
- Robert Attard
- Robert Ellen
- Robert Malko
- Rodrigo Heinzen de Moraes
- Roman Wagner
- Ross Bratton
- Ross Cousens
- Ruslan Ustitc
- Ryan M. Moore
- Sam Aaron
- Sam Mercer
- Sami Fouad
- Sammy Isseyegh
- Samu Kumpulainen
- Santi Lertsumran
- Saša Jurić
- Scott Trinh
- Scott Wey
- Sean Jensen-Grey
- Sebastian Porto
- sekun
- Seve Salazar
- Shane Poppleton
- shayan javani
- Shuqian Hon
- silver-shadow
- Simon Curtis
- Simone Vittori
- Siraj
- Stephen Belanger
- Szymon Wygnański
- Sławomir Ehlert
- TheHiddenLayer
- Theo Harris
- Thomas
- Thomas Ernst
- thorhj
- Timo Sulg
- Tom Schuster
- Tomasz Kowal
- tommaisey
- trag1c
- Tristan de Cacqueray
- Tristan Sloughter
- Vassiliy Kuzenkov
- Vic Valenzuela
- Victor Rodrigues
- Vincent Costa
- Viv Verner
- Vladislav Botvin
- Volker Rabe
- Weizheng Liu
- Wesley Moore
- Willyboar
- Wilson Silva
- xhh
- xxKeefer
- Yamen Sader
- Yasuo Higano
- zahash
- Zsombor Gasparin
- ~1847917
- Šárka Slavětínská
Thanks for reading, I hope you have fun with Gleam! 💜