Published 06 May, 2021 by Louis Pilfold
Gleam is a type safe and scalable language for the Erlang virtual machine, and today v0.15.0 has been released! Let’s take a look at some of the highlights.
More concise tuples
A common bugbear of Gleam developers so far has been that the tuple literal syntax is rather verbose.
pub fn main() -> tuple(Int, Float, String) {
tuple(1, 2.0, "three")
}
After a good deal of discussion on GitHub and on Discord we settled on this new syntax inspired by the ECMAScript TC39 proposal and the tuple syntax of popular programming languages such as Python, Rust, and Haskell.
pub fn main() -> #(Int, Float, String) {
#(1, 2.0, "three")
}
The Gleam compiler will continue to accept both syntaxes for now, with the
tuple(..)
syntax being removed at a later date. To automatically upgrade your
code to the new syntax run gleam format
within your project.
Thank you to Sebastian Porto for kicking off the discussion and suggesting the new syntax, Marcin Puc for implementing the new syntax, and everyone who contributed to the discussion.
Importable prelude module
Gleam’s prelude is where the core data types of Gleam are defined, such as
Int
, Result
, and String
. The contents of the prelude were automatically
imported into every module, and there was no way to import them a second time.
This proved a problem in situations where a module defines a new type or value constructor with the same name as one of the items from the prelude, as it would no longer be possible to refer to the prelude one.
pub type TuringBool {
True // Bool's True being shadowed
False // Bool's False being shadowed
Dunno
}
pub fn does_it_halt(program) -> TuringBool {
Dunno
}
pub fn is_problem_solvable() -> Bool {
False // Wrong bool type!
}
Now with v0.15.0 the prelude module can be imported using the name gleam
,
enabling qualified use of prelude items.
import gleam
// ...
pub fn problem_is_solvable() -> Bool {
gleam.False // Use the prelude False
}
Making assignments expressions
Assignments in Gleam are now expressions that return the value being assigned. Previously in Gleam assignments were statements, meaning they did not evaluate to anything and could not be the final form in a function or a block. This had two main drawbacks.
The first is an development ergonomics issue. When writing Gleam code it is often desirable to save as often as possible so that the type checker can inform you of any mistakes or inconsistencies in the code that has just been written.
pub fn main() {
let x = run(1, 2, 3)
// And now I've hit save...
}
If I write the code above and hit save I might expect the Gleam compiler to tell
me whether I’ve used the run
function correctly. Instead it would complain
of a syntax error, leaving me none the wiser as to whether my code is correct
until I add a placeholder value on the next line and hit save again. With this
latest version of Gleam this code will now be accepted and type checked.
This is a minor change but it removes friction from the development workflow for users with these editing habits, making Gleam friendlier and easier to pick up. Developer experience is a first class concern of Gleam, Gleam should be as productive and enjoyable to write as possible.
The next issue is that previously it was not possible to have an assertion as the final form in a function or block, forcing the user to add a superfluous dummy value to be returned.
/// A function that asserts `segments` is non-empty and
/// starts with a slash.
pub fn assert_valid(segments) {
assert ["/", ..] = segments
}
Typically we prefer to use the Result
type to safely handle invalid states,
but in some scenarios it is beneficial to take advantage of the Erlang virtual
machine’s offensive programming and fault tolerance capabilities by using
assertions like this. For example, when prototyping or writing background batch
processing script.
Standard library additions
This release contains a lot of new standard library functions, mostly in the
list
and iterator
modules.
Lists are Gleam’s primary ordered collection type. These new functions have been added for working with lists:
chunk
- for dividing a list into contiguous chunks that share some property.combination_pairs
- for getting the unique pair combinations of the elements in a list.combinations
- for getting unique combinations sets of a given size of the elements in a list.drop_while
- for removing elements from the front of a list that have some property.last
- for getting the final element from a list.map_fold
- for updating each element in a list while carrying from state from element to element.reduce
- for combining all elements in a list into a single value. A specialisation offold
that uses first element in the list as the accumulator.scan
- for combining all elements in a list into a single value while keeping each intemediate value created.sized_chunk
- for dividing a list into contiguous chunks of a certain size.take_while
- for keeping elements from the front of a list while they have some property, discarding the rest.
Iterators are similar to lists, except they are lazily evaluated. This makes them especially useful for processing dataset too large to load into memory at once. or to decreasing latency as the start of a collection can be processed before the rest has loaded.
all
- for determining whether all elements in an iterator have some property.any
- for determining whether any elements in an iterator have some property.chunk
- for dividing an iterator into contiguous chunks that share some property.drop_while
- for removing elements from the front of an iterator that have some property.empty
- for creating an iterator with no elements.index
- for returning each element in an iterator with an index int indicating their position in the iterator.interleave
- for combining two iterators so that their elements are yielded one at a time, alternating from each iterator.intersperse
- for adding a new element between each element in the iterator.iterate
- for creating a new iterator by calling a function repeatedly on an initial value.last
- for getting the final element from an iterator.once
- for creating an iterator that contains a single element given by a function.reduce
- for combining all elements in an iterator into a single value. A specialisation offold
that uses first element in the list as the accumulator.scan
- for combining all elements in an iterator into a single value while keeping each intemediate value created.single
- for creating an iterator that contains a single element.sized_chunk
- for dividing an iterator into contiguous chunks of a certain size.take_while
- for keeping elements from the front of an iterator while they have some property, discarding the rest.zip
- for combining two iterators into a single iterator that yields pairs of elements.
Thank you to Marcin Puc, Sebastian Porto, Robert Attard, and evuez for all their work on the standard library this release.
Speeding up Gleam development
I am very happy to announce that I am returning to full time Gleam development! You can expect features and releases to be larger and more frequent over the coming year, and I cannot wait to share with you what we’ve got coming next! This is an exciting time for Gleam.
Gleam sponsorship is now my primary source of income. If you would like to support me in making Gleam please consider sponsoring Gleam or asking your employer to sponsor Gleam. Every donation makes a difference, no matter how small, so thank you for your help.
Try it out
If you want to try out the new version of Gleam head over to the getting started page. I’d love to hear how you find it and get your feedback so Gleam can continue to improve.
For all the details of this release check out the changelog files:
Thank you
Gleam is made possible by the support of all the people who have sponsored and contributed to the project, so a huge thank you to them. Thank you!
- Adam Bowen
- Adam Mokan
- Adi Iyengar
- Al Dee
- Ali Farhadi
- Andy Thompson
- Arian Daneshvar
- Arno Dirlam
- Ben Myles
- Bruno Michel
- Chew Choon Keat
- Chris Lloyd
- Chris Young
- christian espinoza
- Christian Meunier
- clangley
- Clever Bunny LTD
- Cole Lawrence
- Connor Lay (Clay)
- Cristine Guadelupe
- Dan Mueller
- Danny Lettuce
- Dave Cottlehuber
- Dave Lucia
- David Bernheisel
- David McKay
- David Pedersen
- Dennis Dang
- Drew Varner
- Edgar Gomes
- Eric Meadows-Jönsson
- Erik Terpstra
- evuez
- Florian Kraft
- Geoffrey Huntley
- Gitpod
- Guilherme Pasqualino
- guimcaballero
- Herdy Handoko
- human154
- Ian González
- Ingmar Gagen
- inoas
- Ivar Vong
- James MacAulay
- Jechol Lee
- Jeff Kreeftmeijer
- jiangplus
- Joe Corkerton
- John Palgut
- josh rotenberg
- José Valim
- João Veiga
- Jussi Norlund
- Kapp Technology
- Lars Lillo Ulvestad
- Lars Wikman
- Leandro Cesquini Pereira
- Luis Hernandez
- Marcel Lanz
- Marcin Puc
- Mariano Guerra
- Mario Vellandi
- Mark Markaryan
- Markus
- Martin Karlsson
- Matthew Cheely
- Memo
- Michael Jones
- Michał Kowieski
- Michał Łępicki
- Michele Riva
- Mike Roach
- Milad
- mrdimosthenis
- Nick Reynolds
- NicklasXYZ
- NineFX
- OldhamMade
- Parker Selbert
- Patrick Ryan
- Pete Jodo
- Peter Saxton
- porkbrain
- Praveen Perera
- qingliangcn
- Raphael Megzari
- Raúl Chouza
- Redmar Kerkhoff
- Reio
- René Klačan
- RJ Dellecese
- Robert Attard
- Robin Mattheussen
- Sam Aaron
- Sascha Wolf
- Saša Jurić
- Scott Wey
- Sean Jensen-Grey
- Sebastian Porto
- Shane Sveller
- Shritesh Bhattarai
- Shunji Lin
- Simone Vittori
- SkunkWerks GmbH
- Syukron Rifail M
- T. Clain
- Terje Bakken
- Tim Buchwaldt
- Tim Condit
- Tomasz Kowal
- Tomochika Hara
- Topher Hunt
- Tristan Sloughter
- Tyler Wilcock
- tynanbe
- Vladimir Kuznetsov
- Wesley Moore
- Wojtek Mach
- YourMother-really
- Yu Matsuzawa
Thanks for reading! Have fun! 💜