Published 08 Mar, 2025 by Louis Pilfold
Gleam is a type-safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.9.0 has been published. Let’s take a look!
Echo debug printing
There are debuggers you can use with Gleam, however the most popular ways to understand the runtime behaviour of a Gleam program is through writing tests and through print debugging.
The standard library function io.debug
is most commonly used for print
debugging. It takes a value of any type, uses target specific runtime
reflection to turn it into a string of Gleam syntax, and then prints it to
standard-error. This works well, but there are some ways in which this function
is not optimal:
- Functions don’t know anything about where they are called from, so it’s not
possible to print the location of the
io.debug
call along with the value being printed. - There’s no way for the compiler or build tool to know this is a special function which is only for debugging, so they are unable to prevent you from accidentally publishing packages that still have debug code in them.
- Runtime reflection is used to make a string representation of the value, so
any information that no longer exists after compile time cannot be used. This
results in
io.debug
relying on heuristics to decide how to show the value, and this can be at-times incorrect.
To improve on this the echo
keyword has been introduced. Prefix an expression
with it and the value will be printed to standard-error, along with the path to
the file and line containing the echo expression, so you can click to jump to it
in your editor.
pub fn main() {
echo [1, 2, 3]
}
It can also be used in pipelines. Here the list returned by the first list.map
call will be printed.
pub fn main() {
[1, 2, 3]
|> list.map(fn(x) { x + 1 })
|> echo
|> list.map(fn(x) { x * 2 })
}
The build tool is aware this is for debugging, so it’ll let you know if you forget to remove it before publishing a package for others to use.
Currently it uses the same runtime reflection and heuristics to format the output, but in future it’ll be enhanced to make use of the compiler’s static analysis.
Thank you Giacomo Cavalieri! We had all manner of annoying CI-related problems when implementing the integration tests for this feature, Jak’s a very patient and determined programmer indeed!
Git dependencies
There are times when we wish to try out early prototype versions of libraries we have made in our applications. It may be tempting to publish these unfinished packages to the package manager, but this would be inappropriate! Only production-ready packages should be published for other people to use, the Gleam package ecosystem is to be high quality and easy to depend upon safely.
As a more suitable alternative the build tool now supports depending on packages within git repositories. Add the git or HTTP URL of a repository along with a tag, branch, or commit SHA, and the build tool will download it for you and then treat it like a regular Gleam dependency package.
[dependencies]
gleam_stdlib = { git = "https://github.com/gleam-lang/stdlib.git", ref = "957b83b" }
Thank you Surya Rose for this much anticipated feature.
More powerful bit arrays on JavaScript
Gleam’s bit array syntax allows you to construct and parse binary data in a way that may be easier to understand than using binary operators. Historically bit arrays had to be byte aligned, meaning they had to have a number of bits that was divisible by 8. Richard Viney has done some incredible work and lifted this limitation. Thank you Richard!
Surya Rose has also been lifting JavaScript bit array restrictions, enabling the usage of dynamically sized segments in bit array patterns. Thank you Surya!
Faster list pattern matching on JavaScript
The list type is one of Gleam’s primary data structures, it is used very heavily in Gleam programs. yoshi~ has been working hard to identify potential performance optimisations for the virtual-DOM implementation of the Lustre framework, and in the process they identified a way to improve the JavaScript code we generate when pattern matching on lists.
Programs that compile to JavaScript and make heavy use of list prefix patterns may now be up to twice as fast!
Go-to type definition
Gleam’s built-in language server brings IDE-like functionality to all editors that support the language server protocol. It has had support for go-to definition for some time, but with this release Giacomo Cavalieri has added support for go-to type definition. Place your cursor on an expression and trigger the feature and the language server will identify the types of all the values used in the expression and present their definitions for you to view and to jump to. Thank you Jak!
HexDocs search integration
When a Gleam package is published HTML documentation is generated and published to HexDocs for users to read. HexDocs have been improving their search functionality to search for types and functions with in packages themselves, and Diemo Gebhardt has updated Gleam’s documentation generator to implement the search index interface so Gleam packages are included in the search. Thank you Diemo!
Another option for searching within Gleam packages is Gloogle, a Gleam community project, which can even search by function type signature.
Custom CA certificates support
Some enterprise networks may perform TLS interception for security reasons. In these environments custom CA certificates must be used as otherwise requests will get TLS errors due to the unknown authority of the injected certificates.
The new GLEAM_CACERTS_PATH
environment variable can be used to specify a
path to CA certificates for Gleam to use when interacting with the Hex package
manager servers, making Gleam usable in these enterprise environments.
Thank you winstxnhdw!
Convert to and from pipeline code actions
Gleam’s much-loved pipe syntax gives programmers another way to write nested function calls so that they read top-to-botton and left-to-right.
Two new language server code actions have been added to help you refactor between the pipe syntax and regular function call syntax. Triggering them on these two code examples will edit your code to match the other.
import gleam/list
pub fn main() {
list.map([1, 2, 3], double)
}
import gleam/list
pub fn main() {
[1, 2, 3] |> list.map(double)
}
You can also choose to pipe arguments other than the first by selecting them in your editor before triggering the code action. Thank you Giacomo Cavalieri!
Generate JSON encoder code action
Many Gleam programs make use of JSON, a common text-based data exchange format.
With this release the Gleam language server now offers a code action to generate
a function to convert a type into JSON using the gleam_json
library. Given
this type definition:
pub type Person {
Person(name: String, age: Int)
}
If the code action is triggered on the type definition this function will be generated:
import gleam/json
fn encode_person(person: Person) -> json.Json {
json.object([
#("name", json.string(person.name)),
#("age", json.int(person.age)),
])
}
Thank you Surya Rose for this code action!
Inline variable code action
The Gleam language server now offers a code action that will inline a variable that is used only once.
import gleam/io
pub fn main() {
let greeting = "Hello!"
io.println(greeting)
}
If the code action is triggered on the greeting
variable the code will be
edited like so:
import gleam/io
pub fn main() {
io.println("Hello!")
}
Thank you Surya Rose!
Generate multi-variant decoder code action
Gleam’s Dynamic
type represents data of unknown shape that comes from outside
of the Gleam program, for example data sent to a HTTP server as JSON. To convert
data from dynamic into known Gleam types the decoder API is used.
A previous release added convenient a code action which would generate a dynamic decoder for a given type definition. With this release this code action has been extended to support multi-variant custom types. For example, given this type definition:
pub type Person {
Adult(age: Int, job: String)
Child(age: Int, height: Float)
}
If the code action is triggered on it then this function will be generated:
import gleam/dynamic/decode
fn person_decoder() -> decode.Decoder(Person) {
use variant <- decode.field("type", decode.string)
case variant {
"adult" -> {
use age <- decode.field("age", decode.int)
use job <- decode.field("job", decode.string)
decode.success(Adult(age:, job:))
}
"child" -> {
use age <- decode.field("age", decode.int)
use height <- decode.field("height", decode.float)
decode.success(Child(age:, height:))
}
_ -> decode.failure(todo as "Zero value for Person", "Person")
}
}
Thank you Surya Rose!
String interpolate code action
The language server now offers a convenient code action to interpolate a value into a string easily. If the cursor is inside a literal string the language server will offer to split it:
"wibble | wobble"
// ^ Triggering the action with the cursor
// here will produce this:
"wibble " <> todo <> " wobble"
And if the cursor is selecting a valid Gleam name, the language server will offer to interpolate it as a variable:
"wibble wobble woo"
// ^^^^^^ Triggering the code action if you're
// selecting an entire name, will produce this:
"wibble " <> wobble <> " woo"
Thank you Giacomo Cavalieri for this!
Module qualifier hovering
The language server can now show documentation for a module when hovering the module qualifier of an imported type or value. Thank you Surya Rose!
Redundant function capture removal
Gleam’s function capture syntax is a shorthand for creating an anonymous function that takes one argument and calls another function with that argument and some other values. These two expressions are equivalent:
let double = fn(number) { int.double(number, 2) }
let double = int.double(_, 2)
If the function capture syntax is used without any additional arguments, then it is redundant and does nothing that referring the function directly wouldn’t do.
let print = io.print(_)
The Gleam code formatter will now remove the redundant function capture syntax for you, formatting it like so:
let print = io.print
Thank you Giacomo Cavalieri!
And the rest
And thank you to the bug fixers: Giacomo Cavalieri, LostKobrakai, Louis Pilfold, Mikko Ahlroth Pedro Francisco, PgBiel, 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 or core team members Giacomo Cavalieri and Surya Rose on GitHub Sponsors.
Thank you to all our sponsors! And especially our top sponsor: Lambda.
- Aaron Gunderson
- Abdulrhman Alkhodiry
- Abel Jimenez
- ad-ops
- Adam Brodzinski
- Adam Johnston
- Adam Wyłuda
- Adi Iyengar
- Adrian Mouat
- Ajit Krishna
- Aleksei Gurianov
- Alembic
- Alex
- Alex Houseago
- Alex Manning
- Alex Viscreanu
- Alexander Koutmos
- Alexander Stensrud
- Alexandre Del Vecchio
- Ameen Radwan
- Andrea Bueide
- AndreHogberg
- Andrew Muratov
- Antharuu
- Anthony Khong
- Anthony Maxwell
- Anthony Scotti
- Arthur Weagel
- Arya Irani
- Azure Flash
- Barry Moore
- Bartek Górny
- Ben Martin
- Ben Marx
- Ben Myles
- Benjamin Kane
- Benjamin Peinhardt
- bgw
- Bjarte Aarmo Lund
- Bjoern Paschen
- Brad Mehder
- Brendan P.
- brettkolodny
- Brian Dawn
- Brian Glusman
- Bruce Williams
- Bruno Michel
- bucsi
- Cam Ray
- Cameron Presley
- Carlo Munguia
- Carlos Saltos
- Chad Selph
- Charlie Duong
- Charlie Govea
- Chew Choon Keat
- Chris Donnelly
- Chris King
- Chris Lloyd
- Chris Ohk
- Chris Rybicki
- Christopher David Shirk
- Christopher De Vries
- Christopher Dieringer
- Christopher Jung
- Christopher Keele
- CJ Salem
- clangley
- Clifford Anderson
- Coder
- Cole Lawrence
- Colin
- Comamoca
- Constantin (Cleo) Winkler
- Corentin J.
- Daigo Shitara
- Damir Vandic
- Dan Dresselhaus
- Dan Strong
- Danielle Maywood
- Danny Arnold
- Danny Martini
- Dave Lucia
- David Bernheisel
- David Cornu
- David Sancho
- Dennis Dang
- dennistruemper
- Diemo Gebhardt
- Dillon Mulroy
- Dima Utkin
- Dmitry Poroh
- DoctorCobweb
- Donnie Flood
- ds2600
- Dylan Carlson
- Éber Freitas Dias
- Ed Hinrichsen
- Edon Gashi
- eeeli24
- Eileen Noonan
- eli
- Emma
- EMR Technical Solutions
- Endo Shogo
- Eric Koslow
- Erik Terpstra
- erikareads
- ErikML
- erlend-axelsson
- Ernesto Malave
- Ethan Olpin
- Evaldo Bratti
- Evan Johnson
- evanasse
- Fabrizio Damicelli
- Fede Esteban
- Felix Mayer
- Fernando Farias
- Filip Figiel
- Florian Kraft
- Francis Hamel
- frankwang
- G-J van Rooyen
- Gabriel Vincent
- Geir Arne Hjelle
- Georg Hartmann
- George
- Georgi Martsenkov
- ggobbe
- Giacomo Cavalieri
- Giovanni Kock Bonetti
- Graham Vasquez
- grotto
- Guilherme de Maio
- Guillaume Heu
- Guillaume Hivert
- Hammad Javed
- Hannes Nevalainen
- Hannes Schnaitter
- Hans Raaf
- Hayleigh Thompson
- Hazel Bachrach
- Henning Dahlheim
- Henrik Tudborg
- Henry Warren
- Heyang Zhou
- Hubert Małkowski
- human154
- Humberto Piaia
- Iain H
- Ian González
- Ian M. Jones
- Igor Montagner
- Igor Rumiha
- ILLIA NEGOVORA
- Ingrid
- inoas
- Isaac
- Isaac Harris-Holt
- Isaac McQueen
- Ismael Abreu
- István Bozsó
- Ivar Vong
- Jacob Lamb
- Jake Cleary
- Jake Wood
- Jakob Ladegaard Møller
- James Birtles
- James MacAulay
- Jan Pieper
- Jan Skriver Sørensen
- Jean-Adrien Ducastaing
- Jean-Luc Geering
- Jen Stehlik
- jiangplus
- Jimpjorps™
- Joey Kilpatrick
- Joey Trapp
- Johan Strand
- John Björk
- John Gallagher
- John Pavlick
- John Strunk
- Jojor
- Jon Lambert
- Jonas E. P
- Jonas Hedman Engström
- jooaf
- Joseph Lozano
- Joshua Steele
- Julian Hirn
- Julian Lukwata
- Julian Schurhammer
- Justin Lubin
- Jérôme Schaeffer
- Kemp Brinson
- Kero van Gelder
- Kevin Schweikert
- Kramer Hampton
- Kritsada Sunthornwutthikrai
- Kryštof Řezáč
- Krzysztof G.
- Leandro Ostera
- Lee Jarvis
- Leon Qadirie
- Leonardo Donelli
- lidashuang
- Lily Rose
- liv
- Loïc Tosser
- Lucas Pellegrinelli
- Lukas Bjarre
- Lukas Meihsner
- Luke Amdor
- Luna
- Manuel Rubio
- Marcos
- marcusandre
- Mariano Uvalle
- Marius Kalvø
- Mark Holmes
- Mark Markaryan
- Martijn Gribnau
- Martin Janiczek
- Martin Poelstra
- Martin Rechsteiner
- martonkaufmann
- Matt Champagne
- Matt Heise
- Matt Mullenweg
- Matt Robinson
- Matt Savoia
- Matt Van Horn
- Matthew Whitworth
- Max McDonnell
- metame
- METATEXX GmbH
- Metin Emiroğlu
- Michael Duffy
- Michael Jones
- Michael Mazurczak
- Michael McClintock
- Mikael Karlsson
- Mike Roach
- Mikey J
- MoeDev
- MzRyuKa
- n8n - Workflow Automation
- Natanael Sirqueira
- Nathaniel Knight
- Nayuki
- NFIBrokerage
- Nick Chapman
- Nick Reynolds
- Nicklas Sindlev Andersen
- NicoVIII
- Niket Shah
- Nikolai S. K.
- Ninaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- NineFX
- Nomio
- Ocean
- Olaf Sebelin
- OldhamMade
- Oliver Medhurst
- Oliver Tosky
- optizio
- Patrick Wheeler
- Paul Guse
- Pawel Biernacki
- Pedro Correa
- Pete Jodo
- Peter Rice
- Philpax
- Pierrot
- Qdentity
- Race Williams
- Rasmus
- Ray
- Raúl Chouza
- re.natillas
- Redmar Kerkhoff
- Reilly Tucker Siemens
- Renato Massaro
- Renovator
- Richard Viney
- Rico Leuthold
- Rintaro Okamura
- Ripta Pasay
- Rob
- Robert Attard
- Robert Ellen
- Robert Malko
- Rodrigo Álvarez
- Ronan Harris
- Rotabull
- Rupus Reinefjord
- Ruslan Ustitc
- Ryan Moore
- Sam Aaron
- Sam Zanca
- sambit
- Sammy Isseyegh
- Santi Lertsumran
- Savva
- Saša Jurić
- Scott Trinh
- Scott Weber
- Scott Wey
- Scott Zhu Reeves
- Sean Jensen-Grey
- Sean Roberts
- Sebastian Porto
- sekun
- Seve Salazar
- Shane Poppleton
- Shuqian Hon
- Sigma
- Simone Vittori
- Stefan
- Stefan Hagen
- Steinar Eliassen
- Stephen Belanger
- Steve Powers
- Strandinator
- Sławomir Ehlert
- Theo Harris
- Thomas
- Thomas Coopman
- Thomas Ernst
- Tim Brown
- Timo Sulg
- Tom Schuster
- Tomasz Kowal
- tommaisey
- Travis Johnson
- Tristan de Cacqueray
- Tristan Sloughter
- Tudor Luca
- tymak
- upsidedowncake
- Valerio Viperino
- Vic Valenzuela
- Victor Rodrigues
- Viv Verner
- Volker Rabe
- Walton Hoops
- Weizheng Liu
- wheatfox
- Willyboar
- Wilson Silva
- Xucong Zhan
- Yamen Sader
- Yasuo Higano
- yoshi~
- Zsombor Gasparin
- ZWubs
- ~1814730
- ~1847917
- ~1867501