Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.16.0 has been published, let's go over what's new.
Gleam gets source maps
Gleam's robust type system makes runtime errors very rare, but it would be naive to say that it's never necessary to debug Gleam code at runtime. When compiling code to JavaScript one challenge for runtime debugging is that the error and the source code shown is that of the compiled JavaScript, not the Gleam the programmer actually wrote.
The solution to this problem is source maps, additional files that Gleam's compiler can now generate, teaching browsers and JavaScript runtimes what the original code is and how it maps on to the JavaScript it runs. Here it is in action with Safari's dev-tools:
As you may know, it's not limited to just displaying the code, it also translates exception stack traces to the original locations, and enables use of breakpoints and debuggers.
To enable source maps set the javascript.source_maps property to true in
your gleam.toml, and ensure that you are serving these new files to the web
browser along with your JavaScript code. If you are serving the build directory
or using Lustre's dev-tools then there's nothing else
to do.
name = "my_project"
version = "1.0.0"
[javascript]
source_maps = true
[dependencies]
gleam_stdlib = ">= 1.0.0 and < 2.0.0"
A huge thank you to Ameen Radwan for this big improvement. Folks who enjoy breakpoint debugging will be especially pleased.
Package-level fault tolerance
Gleam is a fully-typed language, so to compile a module of Gleam code the compiler must first successfully compile all the modules it imports in order to have the required type information. Attempting to analyse modules without this information would result in a cascading failure of incorrect and misleading error messages, so when a module cannot be compiled the build tool stops and reports the errors.
This behaviour successfully prevents the error cascade, but it has been a source of confusion at times. Some developers may not realise they have an error upstream in their project and so do not understand why the language server is providing minimal assistance, presuming that it is broken.
Worse still, because compilation stops at the first failed module the language server may be unable to help with modules that do not directly or indirectly import an invalid module, but just happen to come after them in the compilation order. There's no justifiable reason why these modules could not have been compiled, but the developer is stuck without them.
These two pain points have now been resolved. If the programmer opens in their editor a module that genuinely could not be compiled, then the language server will display a diagnostic on the import that leads to the problem, removing the confusion.
When a module fails to compile the build tool will prune that sub-tree of modules and move on to the next set of modules that can be compiled, ensuring that the programmer has as much up-to-date information as possible available to them in their editor.
Thank you Giacomo Cavalieri! This will make a big difference to some Gleam programmers.
Faster string pattern matching
The compiler now emits more efficient code when matching on single-character
string prefixes on the JavaScript target. For example, glance (a Gleam
package that can parse Gleam code) is now nearly 30% faster on the JavaScript
target:
# before:
min: 10.8ms, max: 365.82ms, median: 14.74ms, mean: 14.76ms
warmup: 100/1.5s, total post-warmup: 1000/14.76s
# after:
min: 8.96ms, max: 143.76ms, median: 10.72ms, mean: 11.06ms
warmup: 100/1.24s, total post-warmup: 1000/11.06s
Thank you Rebecca Reusch and Surya Rose!
Record update fault tolerance
To provide a good language server experience with invalid code, Gleam's compilation aims to be fault-tolerant, and will attempt to continue analysing the code after finding an error.
With this release the analysis of record update expressions is now fault- tolerant, and the compiler now shows a better error message when trying to use the record update syntax with variants that have no labelled fields.
Thank you Giacomo Cavalieri!
Oversized int pattern warning
The largest size an int can have on JavaScript is 52 bits, after which precision is lost. The compiler now raises a warning on the JavaScript target when a pattern attempts to extract an int larger than JavaScript supports, to avoid unexpected results.
warning: Truncated bit array segment
┌─ /src/app/warning.gleam:3:20
│
3 │ let <<_, number:152>> = sha
│ ^^^
This segment is a 152-bit long int, but on the JavaScript target
numbers have at most 52 bits. It would be truncated to its first 52 bits.
Hint: Did you mean to use the `bytes` segment option?
Thank you Giacomo Cavalieri!
Spoiling practical jokes
The compiler now emits a helpful error message when source code contains an invalid unicode character that looks similar to a correct character.
error: Syntax error
┌─ /src/parse/error.gleam:1:20
│
1 │ pub fn main() { #(1‚ 2) }
│ ^ Unexpected character
This looks like ascii comma, but it is actually the unicode low single
comma quotation mark.
Bad news for mischievous pranksters, good news for folks who don't like debugging unexpected parse errors.
Hex package ownership management
Niklas Kirschall has added the new
gleam hex owner add command, allowing folks to add additional owners to their
Hex packages. Thank you Niklas!
Hex package manager error message improvements
The build tool now produces a nicer error message when trying to add a package
version that doesn't exist. For example, running gleam add wisp@11
will now produce:
error: Dependency resolution failed
The package `wisp` has no versions in the range >= 11.0.0 and < 12.0.0.
Thank you Giacomo Cavalieri!
David Matz has also updated our error handling for the new error types recently added to Hex's API, fixing some incorrect and misleading messages that we would show due to attempting to parse them as the old errors. Thank you David!
Add and remove anonymous function code actions
Gleam is a functional programming language, so functions are very commonly used as values, and are passed as arguments to other functions. Sometimes an anonymous function is used.
let add_item = fn(item) { catalog.record(item) }
Other times the function is referenced directly.
let add_item = catalog.record
These two expressions are equivalent, and the programmer would pick between the two depending on what they find to be clearer at each point in their code.
To make writing Gleam even nicer the language server now offers code actions to switch between the two syntaxes. For example:
pub fn main() {
[-1, -2, -3] |> list.map(fn(a) { int.absolute_value(a) })
// ^^ Activating the "Remove anonymous function"
// code action here
}
Would result in this code:
pub fn main() {
[-1, -2, -3] |> list.map(int.absolute_value)
}
And with this code the other action could be used to reverse the change.
Thank you Eli Treuherz!
Extract function code action improvements
Gleam's language server has an "extract function" code action that can be used to… well… extract a function.
It can now be used in pipelines to extract a part of one into a function. For example, triggering it here:
pub fn words(phrase: String) -> List(String) {
phrase
|> string.lowercase
// ^^^
|> string.replace(each: "jak", with: "lucy")
// ^^^ selecting these two steps of the pipeline
|> string.split(on: " ")
}
Would result in the following code:
pub fn words(phrase: String) -> List(String) {
phrase
|> function
|> string.split(on: " ")
}
fn function(string: String) -> String {
string
|> string.lowercase
|> string.replace(each: "jak", with: "lucy")
}
It has also been improved to work better with assignments, extracting the assigned value. For example:
pub fn personal_blog() {
let introduction =
html.main([], [
html.h1([], [html.text("Hello, world!")]),
html.p([], [html.text("Gleam is cool")])
])
//^^^ Triggering "extract function" on this expression
html.body([introduction, blog_posts()])
}
Would result in the following code:
pub fn personal_blog() {
let introduction = function()
html.body([introduction, blog_posts()])
}
pub fn function() {
html.main([], [
html.h1([], [html.text("Hello, world!")]),
html.p([], [html.text("Gleam is cool")])
])
}
Thank you Giacomo Cavalieri!
Replace _ with type code action
Gleam's type hole syntax allows to omit parts of a type annotation, putting no restrictions on what that part of the type can be inferred as. This can be convenient when sketching out new code, but it's best practice to have full annotations to make the code and the intended behaviour of the code easier to understand.
To aid with moving to full annotation the language server has a code action to replace a type hole with the underlying type. For example:
pub fn load_user(id: Int) -> Result(_, Error) {
sql.find_by_id(id)
|> result.map_error(CannotLoadUser)
}
Triggering the code action over the _ will result in the following code:
pub fn load_user(id: Int) -> Result(User, Error) {
sql.find_by_id(id)
|> result.map_error(CannotLoadUser)
}
Thank you Giacomo Cavalieri!
Constant list prepending
The compiler now supports list prepending in constants. For example:
pub const viviparous_mammals = ["dog", "cat", "human"]
pub const all_mammals = ["platypus", "echidna", ..viviparous_mammals]
Thank you Surya Rose!
Language server completion improvements
The language server's completion capability has been improved to understand Gleam code more precisely. It now shows completions for the labelled argument of a record when writing a record update, and no longer shows completions for values when editing a qualified type.
Thank you Giacomo Cavalieri!
Tuple formatting improvement
Gleam has a source code formatter, automating the business of formatting your code, so you can focus on your task, and not get stuck in another argument about whether or not you should use a trailing comma.
With this release the formatting of long nested tuples has been improved. Previously the formatter would split only the last tuple, but now it favours first splitting each element onto its own line:
// Old format
#(#(12345, wobble), #(
some_long_tuple,
"passed_as_last_argument"
))
// New format:
#(
#(12345, wobble),
#(some_long_tuple, "passed_as_last_argument")
)
Thank you to formatter wizard Giacomo Cavalieri!
More external code source extension support
Gleam's external type and external function features allow Gleam code to make use of code written in other languages on the same runtime, and Gleam's build tool will process source files of these languages that are included in a source directory of a Gleam project.
Some JavaScript runtimes now have built-in support for TypeScript and JSX, so
the build tool has had support for including files with mts, cts, jsx,
and tsx extensions.
Thank you Niklas Kirschall!
And the rest
And thank you to the bug fixers and experience polishers: Andrey Kozhev, Daniele Scaratti, Eyup Can Akman, Giacomo Cavalieri, Giovanni Maria Zanchetta, John Downey, Louis Pilfold, Lucy McPhail, Luka Ivanović, Niklas Kirschall, 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 special thanks to our top sponsor:
- Aaron Gunderson
- Abel Jimenez
- Aboio
- ad-ops
- Adam Brodzinski
- Adam Daniels
- Adam Johnston
- Adi Iyengar
- Adrian Mouat
- Ajit Krishna
- albertchae
- Aleksei Gurianov
- Alembic
- Alex Houseago
- Alex Kelley
- Alex Manning
- Alexander Stensrud
- Alexandre Del Vecchio
- Aliaksiej Homza
- Alistair Smith
- Ameen Radwan
- Andrey
- André Mazoni
- Andy Young
- Antharuu
- Anthony Scotti
- Antonio Farinetti
- Arthur Weagel
- Arya Irani
- Baqtiar
- Barry Moore II
- Ben Martin
- Ben Marx
- Ben Myles
- Benjamin Kane
- Benjamin Moss
- bgw
- Billuc
- Bjarte Aarmo Lund
- Bjoern Paschen
- blurrcat
- Brad Mehder
- Brett Cannon
- Brett Kolodny
- Brian Glusman
- Bruce Williams
- Bruno Konrad
- bucsi
- Cameron Presley
- Carlo Munguia
- Carlos Saltos
- Chad Selph
- Charlie Govea
- Charlie Tonneslan
- Chew Choon Keat
- Chris Lloyd
- Chris Ohk
- Chris Olsen
- Chris Vincent
- Christian Visintin
- Christopher David Shirk
- Christopher De Vries
- Christopher Dieringer
- Christopher Keele
- Clifford Anderson
- Coder
- Cole Lawrence
- Comamoca
- Constantin (Cleo) Winkler
- Corentin J.
- Cris Holm
- Curling IO
- dagi3d
- Damir Vandic
- Dan
- Dan Dresselhaus
- Dan Gieschen Knutson
- Dan Piths
- Dan Strong
- Daniele
- Daniil Nevdah
- Danny Arnold
- Danny Martini
- Dave Lucia
- David Bernheisel
- David Cornu
- David Matz
- David Pendray
- Diemo Gebhardt
- Djordje Djukic
- Donnie Adams
- Dylan Anthony
- Dylan Carlson
- Ed Rosewright
- Edon Gashi
- Eileen Noonan
- Eli Treuherz
- Emma
- Eric Koslow
- Erik Ohlsson
- Erik Terpstra
- erikareads
- ErikML
- erlend-axelsson
- Ernesto Malave
- Ethan Olpin
- Evaldo Bratti
- Evan Johnson
- evanasse
- Eyüp Can Akman
- Fabrizio Damicelli
- Falk Pauser
- Fede Esteban
- Felix
- Fernando Farias
- Filip Figiel
- Filip Hoffmann
- Florian Kraft
- Francis Hamel
- frankwang
- G-J van Rooyen
- Gabriela Sartori
- Gavin Morrow
- Gears
- Geir Arne Hjelle
- George Grec
- Giacomo Cavalieri
- ginkogruen
- GioMaz
- Giovanni Kock Bonetti
- Graham
- Grant Everett
- Guilherme de Maio
- Guillaume Heu
- Gungun974
- Hannes Nevalainen
- Hans Raaf
- Hari Mohan
- Harry Bairstow
- Hazel Bachrach
- Henning Dahlheim
- Henrik Tudborg
- Henry Warren
- Hizuru3
- Hubert Małkowski
- Iain H
- Ian González
- Ian M. Jones
- Igor Montagner
- inoas
- Isaac
- Isaac Harris-Holt
- Isaac McQueen
- iskrisis
- Ivar Vong
- Jachin Rupe
- Jake Cleary
- Jake Wood
- James Birtles
- James MacAulay
- Jan Fooken
- Jan Pieper
- Jan Skriver Sørensen
- Jason Florentino
- Jean Niklas L'orange
- Jean-Adrien Ducastaing
- Jean-Luc Geering
- Jen Stehlik
- Jerred Shepherd
- Jimmy Utterström
- Jimpjorps™
- Joey Kilpatrick
- Joey Trapp
- Johan Strand
- Johanna Larsson
- John Björk
- John Downey
- John Strunk
- Jojor
- Jon Charter
- Jon Lambert
- Jonas E. P
- Jonas Hedman Engström
- Jonatan Männchen
- Jonathan D.A. Jewell
- jooaf
- Joseph Lozano
- Joshua Steele
- jstcz
- Julian Hirn
- Julian Lukwata
- Julian Schurhammer
- Justin Lubin
- Jérôme Schaeffer
- Jørgen Andersen
- KamilaP
- Kemp Brinson
- Kero van Gelder
- Kevin Schweikert
- Kile Deal
- Kirill Morozov
- Kramer Hampton
- Kristoffer Grönlund
- Kristoffer Grönlund
- Krzysztof Gasienica-Bednarz
- Kuma Taro
- Landon
- Leah Ulmschneider
- Lee Jarvis
- Lennon Day-Reynolds
- Leon Qadirie
- Leonardo Donelli
- Lexx
- lidashuang
- Lucy McPhail
- Luka Ivanović
- Lukas Bjarre
- Luke Amdor
- Manuel Rubio
- Mario Vellandi
- Marius Kalvø
- Mark Dodwell
- Mark Holmes
- Mark Markaryan
- Mark Rudolph
- Markus Wesslén
- Martin Janiczek
- Martin Poelstra
- Martin Rechsteiner
- Matt Heise
- Matt Mullenweg
- Matt Savoia
- Matt Van Horn
- Matthew Jackson
- Max Bridges
- Max McDonnell
- metame
- METATEXX GmbH
- Michael Davis
- Michael Duffy
- Michael G
- Michael Jones
- Michael Mazurczak
- Michal Timko
- Mikael Karlsson
- Mike Roach
- Mikey J
- MoeDev
- Moin
- N. G. Scheurich
- n8n - Workflow Automation
- Natalie Rose
- Natanael Sirqueira
- Nessa Jane Marin
- Nick Leslie
- Nicklas Sindlev Andersen
- NicoVIII
- Nigel Baillie
- Niket Shah
- Niklas Kirschall
- Nikolai Steen Kjosnes
- Nikolas
- Ninaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- NineFX
- nkxxll
- NNB
- Noah Betzen
- Nomio
- nshkrdotcom
- nunulk
- Olaf Sebelin
- OldhamMade
- Oliver Medhurst
- Oliver Tosky
- ollie
- Optizio
- P.
- Patrick Wheeler
- Pattadon Sa-ngasri
- Paul Guse
- Pedro Correa
- Pete Jodo
- Peter Rice
- Peter Saxton
- Philpax
- Qdentity
- qexat
- R.Kawamura
- Race
- Ram Prasanth Udhaya Baskar
- Rasmus
- Raúl Chouza
- rebecca
- Redmar Kerkhoff
- Reilly Tucker Siemens
- Renato Massaro
- Renovator
- Richard Viney
- Richy McGregor
- Rico Leuthold
- Rintaro Okamura
- Ripta Pasay
- Rob Durst
- Robert Attard
- Robert Ellen
- Robert Malko
- Rodrigo Álvarez
- Rohan
- Rotabull
- Rupus Reinefjord
- Ruslan Ustitc
- Russell Clarey
- Ryan Davis
- Ryan Moore
- Sakari Bergen
- Sam Aaron
- Sam Zanca
- Sammy Isseyegh
- Samu
- 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
- SR
- Stefan
- Steinar Eliassen
- Stephane Rangaya
- Strandinator
- Sławomir Ehlert
- The Sentience Company
- Thomas
- Thomas Coopman
- Thomas Crescenzi
- Tim Brown
- Timo Sulg
- Tobias Ammann
- Tomasz Kowal
- tommaisey
- Tristan de Cacqueray
- Tristan Sloughter
- Tudor Luca
- unknown
- upsidedowncake
- Valerio Viperino
- Vassiliy Kuzenkov
- Viv Verner
- Volker Rabe
- Walton Hoops
- Will Ramirez
- Wilson Silva
- Yamen Sader
- Yasuo Higano
- Zsolt Kreisz
- ZWubs
- ~1847917
- ~1867501
- Éber Freitas Dias
- 音㦡