Merry Christmas and happy holidays everyone! Gleam is a type-safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Gleam v1.14.0 is our Xmas gift for you, let's unwrap it and take a look inside.
External annotations for external types
Gleam doesn't exist in a vacuum, instead builds upon the existing Erlang and JavaScript ecosystems. It's not uncommon for Gleam programs and libraries to internally make use of code written in other languages in the same ecosystem, and Gleam code can be called from those languages too. To help with the interoperability between these languages the Gleam compiler can generate Erlang type specifications and TypeScript declaration files, which can be used with external type checkers and static analysis tooling to ensure the various languages are calling each other's code correct.
// Function written in Gleam
pub fn greet(name: String) -> String {
"Hello, " <> name <> "!"
}
% Generated Erlang type spec
-spec greet(binary()) -> binary().
// Generated TypeScript type definition
export function greet(name: string): string;
When writing bindings to code in other languages, Gleam's external type feature is used to declare a type that can be referenced in Gleam, but since it originates from outside of Gleam it has an unknown shape and cannot be constructed directly. A very useful feature for designing Gleam-y APIs for external code! The limitation here was that due to Gleam not knowing anything about these external types, it was not able to produce a precise definition in generated Erlang or TypeScript type definitions, having to fall back to the correct but vague "any" type of each language.
The @external annotation is now supported for external types, giving the
programmer a way to specify an Erlang or TypeScript type definition to be used.
For example, the Dict type from the standard library can now be written
as the following:
// External type declared in Gleam
@external(erlang, "erlang", "map")
@external(javascript, "../dict.d.mts", "Dict")
pub type Dict(key, value)
% Generated Erlang type definition
-type dict(K, V) -> erlang:map(K, V).
// Generated TypeScript type definition
import type { Dict } from "../dict.d.mts";
export type Dict$<K, V> = Dict<K, V>;
Thank you Surya Rose and Yoshie for this!
Interference-based pruning for ints
Gleam uses pattern matching for flow control, a common feature of functional
languages. This is where the programmer declares a series of patterns in a
case expression, and the first clause has a pattern that matches the shape of the
data being matched upon will be executed. Because these patterns are
declarative the compiler can generate more optimised code than it would be able
to for an if-else chain of boolean checks.
The previous release introduced interference-based pruning, a sophisticated optimisation that improves performance and detects more redundant patterns when pattern matching on binary data. This release extends this optimisation to work with int segments too, further increasing its effectiveness!
case bits {
<<"a">> -> 0
<<97>> -> 1
// ^- This clause is unreachable because it's equal to "a".
<<0b1:1, _:1>> -> 2
<<0b11:2>> -> 3
// ^- This clause is unreachable because of the previous
_ -> 99
}
Thank you fruno!
Number normalisation in pattern matching analysis
Numbers can be written in several different formats in Gleam. Most the time they are written in decimal, but they could also be written in octal, hexadecimal, or binary, or scientific notation could be used for floats.
Gleam's compiler now internally normalises these values to a single canonical representation. This new representation is now used by the pattern matching analysis engine, further enabling optimisations such as interference-based pruning, making Gleam code faster and detecting more redundant clauses.
Thank you ptdewey!
More precise incorrect subject count error message
Gleam's case expression can be used to match on multiple values at the same
time. If two values are being matched on, then each clause will need two
patterns. If three are matched on then three patterns are needed. Etc.
Providing the wrong number of patterns will result in a compile error, and now
the error has been improved to show exactly where the problem is.
case wibble {
0, _ -> 1
^^^^ Expected 1 pattern, got 2
0 | -> 1
^ I was expecting a pattern after this
}
Thank you fruno!
Fault tolerance
Gleam's compiler implements fault tolerant analysis. This means that when there is some error in the code the compiler can still continue to analyse the code to the best of its ability, ignoring the invalid parts. Because of this, the Gleam language server can have a good understanding of the code and provide IDE features even when the codebase is in an invalid state.
Giacomo Cavalieri made type analysis of constants fault tolerant. Adi Salimgereyev made the field definitions of custom type variants fault tolerant. mxtthias made bool pattern matching syntax errors fault tolerant.
Thank you all!
Faster equality testing
The performance of == and != has been improved for fieldless custom type
variants when compiling to JavaScript. This was done by generating comparison
code specific to the custom type rather than using the generic equality check
code. Thank you Nafi for this improvement!
Redundant module warnings
A Gleam module with no public types or functions can't do anything, so it's not
useful! The compiler now emits a warning when a module contains no public
definitions. The gleam publish command will also return an error for packages
with modules like these, prompting the programmer to remove the module.
Thanks to Vitor Souza!
Detached documentation warnings
Gleam has /// comments for writing documentation, with the comment being
placed immediately above the definition being documented. This syntax is
visually similar to the regular // comment, so occasionally a Gleam
programmer will accidentally place regular comments within documentation,
causing the first part of the documentation to be "detatched", and not show up
in the rendered HTML documentation for that code.
/// This documentation is not attached.
///
// This is regular comment.
///
/// This is the attached documentation.
pub fn wibble() {
todo
}
The compiler now emits a warning when a documentation comment is not attached to a definition due to a regular comment in between, helping to prevent this mistake. Thank you Surya Rose!
Record update syntax for constants
Gleam's record update syntax can now be used in constant definitions, enabling constant records to be constructed from other constant records.
pub const base_http_config = HttpConfig(
host: "0.0.0.0",
port: 8080,
use_tls: False,
log_level: Info,
)
pub const dev_http_config = HttpConfig(
..base_http_config,
port: 4000,
log_level: Debug,
)
pub const prod_http_config = HttpConfig(
..base_http_config,
port: 80,
use_tls: True,
log_level: Warn,
)
Thank you Adi Salimgereyev!
Latest Elixir support
Gleam's build tool can compile Gleam code, but also Erlang and Elixir code as well. This is part of Gleam's commitment to being part of the wider BEAM ecosystem, rather than an independent language that happens to use the same virtual machine. Practically, this means that Gleam programs can add Erlang and Elixir code or packages as dependencies, and not have to do additional work or configuration to use that code.
This release updates to the latest Elixir compiler API, fixing some warnings that would be emitted with previous versions of Gleam and the latest version of Elixir. Thank you Andrey Kozhev!
Outdated dependencies view
It is important to take great care when using dependency packages: They can save a lot of time, but all code added to a project has a maintenance cost and security considerations.
To help with this task we have been providing more tools for working with
dependencies, the latest of which is the gleam deps outdated command. This
command makes it much easier to identify which packages have available updates
in the package repository.
$ gleam deps outdated
Package Current Latest
------- ------- ------
wibble 1.4.0 1.4.1
wobble 1.0.1 2.3.0
Thank you Vladislav Shakitskiy!
Helpful git-not-installed error message
Dependency packages can be added from Git repositories as well as from the Hex
package repository. The git program must be installed to use Git
dependencies, and previously if it was not the build tool would fail with a
not-very-helpful error message when trying to download these dependencies.
With this version the error message has been improved, and includes a link to instructions on how to install it.
error: Program not found
The program `git` was not found. Is it installed?
Documentation for installing Git can be viewed here:
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
Thank you Andrey Kozhev!
Further code action support
The gleam binary also includes the Gleam language server, bringing IDE-like
functionality to all editors that support the language server protocol. Several
of the existing code actions (automated refactorings, etc) have been improved
so they can be triggered from more places, or so they support a wider range of
code.
The "inline variable" code action can now trigger when used over the let
keyword of a variable to inline, rather than just the variable name and the
usage of that variable.
The "add omitted labels" code action can now be used in function calls where some of the labels have been provided already. This greatly increases how often this code action is usable, a very impactful improvement!
The "generate function" code action can now trigger when used over constant values, while previously it could only be used in expressions within module functions. Gleam's type information analysis is used to contextually identify when a referenced name is a function, and so when this code action is relevant.
Thank you Giacomo Cavalieri for these!
Qualify and unqualify code actions can now be triggered on constants, instead of only on expressions within module functions. Thank you Vladislav Shakitskiy!
Type directed autocompletion
The Gleam language server provides autocompletion, sending a list of options for the programmer to select from. The options are ordered based upon how likely the programmer is to select each option, with the most likely candidates being presented first. This ordering now takes type information into account, with values that produce the correct type being ranked higher.
Thank you Surya Rose!
Missing patterns order improvement
Gleam performs exhaustiveness checking of case expressions, reporting an error if the patterns do not cover all the possible shapes of the data being matched upon.
error: Inexhaustive patterns
ââ /Users/louis/Desktop/app/src/app.gleam:10:3
â
10 â â case person {
11 â â Student(name:) -> name
12 â â }
â â°âââ^
This case expression does not have a pattern for all possible values. If it
is run on one of the values without a pattern then it will crash.
The missing patterns are:
Teacher(title:, name:)
Visitor(name:)
The missing patterns in error messages and in the code generated by the "add missing patterns" code action used to be ordered lexicographically, but now they will be presented in definition order. This is more commonly the order that the programmer wants, so they are less likely to need to re-order them in their code.
Thank you fruno!
Merge case branches code action
There is a new code action which can merge case clauses that have the same body. For example:
case user {
Admin(name:, ..) -> todo
//^^^^^^^^^^^^^^^^^^^^^^^^
Guest(name:, ..) -> todo
//^^^^^^^^^^^^^^^^ Selecting these two branches you can
// trigger the "Merge case branches" code action
_ -> todo
}
Triggering the code action would result in the following code:
case user {
Admin(name:, ..) | Guest(name:, ..) -> todo
_ -> todo
}
Little automated refactoring like these can make a big difference to the experience of writing and editing Gleam code. Thank you Giacomo Cavalieri!
Improved name selection in code actions
Another useful code action found in the language server is "generate function". It is available when the code is calling an unknown function, and it will generate the skeleton of a function with the correct name, arguments, and types.
To name the argument the language server looks at how it is called, reusing the name of any variables used, or the names of the types if the arguments given are not variables. This name selection has been improved, and will also look at the names of field labels when arguments are provided using the record access syntax.
pub type User {
User(id: Int, name: String)
}
pub fn go(user: User) -> Session {
authenticate(user.id, user.name)
}
Having the language server generate the missing authenticate function will
produce the following code:
pub fn authenticate(id: Int, name: String) -> Session {
todo
}
A similar improvement has been made for the "pattern match on variable" code action when used on tuples. Thank you Giacomo Cavalieri!
Language server rename failure notification
The Gleam language server supports the popular and extremely useful "rename" action. Previously if you attempted to rename something to that would result in a syntax error (e.g. putting a space in a function name) it would not perform the renaming, but it wouldn't tell you why. With this release it will now send a notification for the editor to display, letting the programmer know that it is an invalid name.
Thank you Giacomo Cavalieri!
Improved language server diagnostic grouping
The language server protocol (LSP) is a common interface that lets IDE engines (such as Gleam's language server) work with many different code editors. It's brilliant in what it enables, but unfortunately the specification is vague in some places, so different code editors interpret messages from the server in different ways. This occasionally results in some editors having not-quite providing the experience we intended when editing Gleam.
Recently we discovered that when we emit warnings or errors that point to multiple places in the source code the Zed editor would treat the two locations as independent, so the programmer would need to trigger "go to next diagnostic" to go to the next error. We have now adjusted the design of our diagnostics, bringing Zed inline with the behaviour of the other major editors.
Thank you fruno. Zedlings rejoice!
To be clear, we do not think that Zed was doing anything incorrect here. We love Zed! From our understanding both behaviours are correct due to ambiguity in the LSP specification.
Annotate all top level definitions code action
Gleam has total and perfect type analysis, so it can tell the type of every expression in a project, assuming there are no errors in the code that cause it to fail to compile. Type annotations are optional, and while they don't help the type analysis or make code "more typed", it is considered good style to annotate all module functions to help humans read the code.
The language server has for some time offered a code action for adding missing annotations to a single definition, and now it has another for doing all definitions at once.
pub const answer = 42
pub fn add(x, y) {
x + y
}
pub fn add_one(thing) {
let result = add(thing, 1)
result
}
Triggering the "Annotate all top level definitions" code action on
the name of function add_one results in the following code:
pub const answer: Int = 42
pub fn add(x: Int, y: Int) -> Int {
x + y
}
pub fn add_one(thing: Int) -> Int {
let result = add(thing, 1)
result
}
This will be useful for folks who like to forego writing type annotations when quickly writing new code, or exploring a new design. Thank you Andrey Kozhev!
Command line API improvements
Various ergonomic improvements have been made to the command line interface of Gleam's build tool.
-
The help text displayed by
gleam dev --help,gleam test --help, and
gleam run --help has been improved: now each one states which function it's
going to run. Thanks to Giacomo Cavalieri.
The --invert and --package options of gleam deps tree are now mutually
exclusive; if both options are given the command will fail. Previously,
--invert would be silently ignored if given together with --package.
Thanks to Evan Silberman.
The format used for gleam deps list and the notice of available major
version upgrades has been improved.
And the rest
And thank you to the bug fixers and experience polishers:
Adi Salimgereyev, Elias Haider, fruno, Giacomo Cavalieri, Gurvir Singh, Ioan Clarke, Louis Pilfold, Matias Carlander, Patrick Dewey, Richard Viney, and Surya Rose.
For full details of the many fixes and improvements they've implemented see the changelog.
A call for support
Gleam 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.
We have made great progress towards our goal of being able to appropriately pay the core team members, but we still have further to go. Please consider supporting the project however you can.
Thank you to all our sponsors! And special thanks to our top sponsors:
- Aaron Gunderson
- Abel Jimenez
- Aboio
- abs0luty
- ad-ops
- Adam Brodzinski
- Adam Daniels
- Adam Johnston
- Adam WyĆuda
- Adi Iyengar
- Adrian Mouat
- AĂŻssata MaĂŻga
- Ahmad Alhashemi
- Ajit Krishna
- AkĂn Berges
- Aleksei Gurianov
- Alembic
- Alex Houseago
- Alex Kelley
- Alex Manning
- Alexander Stensrud
- Alexandre Del Vecchio
- Ameen Radwan
- Andrea Bueide
- Andrey
- André Mazoni
- Andy Young
- Antharuu
- Anthony Maxwell
- Anthony Scotti
- Antonio Farinetti
- Arthur Weagel
- Arya Irani
- Azure Flash
- Baqtiar
- Baraich
- Barry Moore II
- Ben Martin
- Ben Marx
- Ben Myles
- Benjamin Kane
- Benjamin Moss
- bgw
- Billuc
- Bjarte Aarmo Lund
- Bjoern Paschen
- bo0tzz
- Brad Mehder
- Brett Cannon
- Brett Kolodny
- Brian Glusman
- Bruce Williams
- Bruno Konrad
- Bruno Michel
- bucsi
- Cam Ray
- Cameron Presley
- Carlo Munguia
- Carlos Saltos
- Chad Selph
- Charlie Govea
- Chew Choon Keat
- Chris Donnelly
- Chris King
- Chris Lee
- Chris Lloyd
- Chris Ohk
- Chris Olsen
- Chris Rybicki
- Chris Vincent
- Christopher David Shirk
- Christopher De Vries
- Christopher Dieringer
- Christopher Jung
- Christopher Keele
- CJ Salem
- Clifford Anderson
- Coder
- Cole Lawrence
- Comamoca
- Comet
- Constantin (Cleo) Winkler
- Constantin Angheloiu
- contra1337
- Corentin J.
- Cris Holm
- dagi3d
- Damir Vandic
- Dan
- Dan Dresselhaus
- Dan Gieschen Knutson
- Dan Strong
- Danielle Maywood
- Danny Arnold
- Danny Martini
- Dave Lucia
- David Bernheisel
- David Cornu
- David Pendray
- Diemo Gebhardt
- Donnie Flood
- Dylan Anthony
- Dylan Carlson
- Ed Hinrichsen
- Ed Rosewright
- Edon Gashi
- Eileen Noonan
- eli
- Elias
- Emma
- Eric Koslow
- Erik Terpstra
- erikareads
- ErikML
- erlend-axelsson
- Ernesto Malave
- erodrigufer
- Ethan Olpin
- Evaldo Bratti
- Evan Johnson
- Evan Silberman
- evanasse
- Fabrizio Damicelli
- Falk Pauser
- Fede Esteban
- Felix
- Fernando Farias
- Filip Figiel
- Florian Kraft
- Francis Hamel
- frankwang
- fruno
- G-J van Rooyen
- Gabriel Vincent
- Gavin Panella
- Gears
- Geir Arne Hjelle
- ggobbe
- Giacomo Cavalieri
- Giovanni Kock Bonetti
- Graham
- Grant Everett
- graphiteisaac
- Guilherme de Maio
- Guillaume Heu
- Guillaume Hivert
- Hammad Javed
- Hannes Nevalainen
- Hans Raaf
- Hari Mohan
- Hazel
- Hazel Bachrach
- Henning Dahlheim
- Henrik Tudborg
- Henry Warren
- Hizuru3
- Hubert MaĆkowski
- Iain H
- Ian GonzĂĄlez
- Ian M. Jones
- idea-list
- Igor Montagner
- inoas
- ioan.clarke
- Isaac Harris-Holt
- Isaac McQueen
- iskrisis
- Ivar Vong
- Jachin Rupe
- Jake Cleary
- Jake Wood
- James Birtles
- James MacAulay
- Jan Pieper
- Jan Skriver SĂžrensen
- Jasmine Tang
- Jean Niklas L'orange
- Jean-Adrien Ducastaing
- Jean-Luc Geering
- Jean-Marc QUERE
- Jen Stehlik
- Jerred Shepherd
- Ji Sungbin (Forky)
- Jimmy Utterström
- Jimpjorpsâą
- Joey Kilpatrick
- Joey Trapp
- Johan Strand
- John Björk
- John Strunk
- Jojor
- Jon Charter
- Jon Lambert
- Jonas E. P
- Jonas GruÌnwald
- Jonas Hedman Engström
- Jonatan MĂ€nnchen
- jooaf
- Joseph Lozano
- Joseph Myers
- Joseph T. Lyons
- Josh Gillies
- Joshua Steele
- Julian Hirn
- Julian Lukwata
- Julian Schurhammer
- Justin Lubin
- JérÎme Schaeffer
- JĂžrgen Andersen
- KamilaP
- Kemp Brinson
- Kero van Gelder
- Kevin Schweikert
- Kristoffer Grönlund
- Krzysztof Gasienica-Bednarz
- Kuma Taro
- Landon
- Leah Ulmschneider
- Lee Jarvis
- Lennon Day-Reynolds
- Leo Ostera
- Leon Qadirie
- Leonardo Donelli
- Lexx
- lidashuang
- Lukas Bjarre
- Luke Amdor
- m.m.kosyakov
- Manuel Rubio
- Mario Vellandi
- Marius KalvĂž
- Mark Dodwell
- Mark Holmes
- Mark Markaryan
- Mark Rudolph
- Martin Janiczek
- Martin Poelstra
- Martin Rechsteiner
- matiascr
- Matt Heise
- Matt Mullenweg
- Matt Savoia
- Matt Van Horn
- Matthew Jackson
- Matthias Nilsson
- Max McDonnell
- metame
- METATEXX GmbH
- Michael Duffy
- Michael Jones
- Michael Lynch
- Michael Mazurczak
- Michal Timko
- Michel Lind
- Mikael Karlsson
- Mike Roach
- Mikey J
- MoeDev
- MzRyuKa
- N. G. Scheurich
- n8n - Workflow Automation
- Nafi
- Natalie Rose
- Natanael Sirqueira
- Nicklas Sindlev Andersen
- NicoVIII
- Niket Shah
- Nikolas
- Ninaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- NineFX
- NNB
- Noah Betzen
- Nomio
- nunulk
- Olaf Sebelin
- OldhamMade
- Oliver Medhurst
- Oliver Tosky
- ollie
- optizio
- orge-dev
- Patrick Wheeler
- Pattadon Sa-ngasri
- Paul Guse
- Pedro Correa
- Pete Jodo
- Peter Rice
- Philpax
- ptdewey
- Qdentity
- Rasmus
- RaĂșl Chouza
- Redmar Kerkhoff
- Reilly Tucker Siemens
- renatillas
- Renato Massaro
- Renovator
- Richard Viney
- Rico Leuthold
- Rintaro Okamura
- Ripta Pasay
- rob durst
- Robert Attard
- Robert Ellen
- Robert Malko
- Rodrigo Ălvarez
- Rohan
- Rotabull
- Rupert Rutledge
- Rupus Reinefjord
- Ruslan Ustitc
- Russell Clarey
- Sakari Bergen
- Sam Aaron
- Sam Zanca
- Sammy Isseyegh
- Samu
- Sanfer
- Savva
- SaĆĄa JuriÄ
- Scott Trinh
- Scott Wey
- Scott Zhu Reeves
- Sean Cribbs
- Sean Roberts
- Sebastian Porto
- Seve Salazar
- Sgregory42
- Shane Poppleton
- Shawn Drape
- Shritesh Bhattarai
- shxdow
- Sigma
- simone
- Stefan
- Steinar Eliassen
- Stephane Rangaya
- Stephen Belanger
- stndrs
- Strandinator
- SĆawomir Ehlert
- Theo Harris
- Thomas
- Thomas Coopman
- Thomas Crescenzi
- Tim Brown
- Timo Sulg
- Tobias Ammann
- Tom Hughes
- Tom Schuster
- Tomasz Kowal
- tommaisey
- Tristan de Cacqueray
- Tristan Sloughter
- Tudor Luca
- upsidedowncake
- Valerio Viperino
- Vassiliy Kuzenkov
- vitor
- Viv Verner
- Volker Rabe
- vpribish
- vshakitskiy
- Walton Hoops
- Weizheng Liu
- Will Ramirez
- Wilson Silva
- Wundersmiths
- Xetera
- Yamen Sader
- Yasuo Higano
- Yoshi Reusch
- Zsolt Kreisz
- Zsombor Gasparin
- ZWubs
- ~1814730
- ~1847917
- ~1867501
- Ăber Freitas Dias
- ĐĐŸŃŃŃ