Published 01 Jun, 2020 by Louis Pilfold
Thanks to all our new contributors Gleam’s development is faster than ever and it’s time for another release! Let’s take a look.
Improved error handling
The main way that errors are represented and handled in Gleam is through the Result type. It’s baked into the language itself, but if you were to implement it in a library it would be implemented like so:
pub type Result(success_value, failure_detail) {
Ok(success_value)
Error(failure_detail)
}
If a function can fail then it returns a Result, wrapping a successful return value in an Ok record, or returning detail on the failure in an Error record if it’s not successful.
// parse_int(String) -> Result(Int, String)
parse_int("123") // -> Ok(123)
parse_int("erl") // -> Error("expected a number, got `erl`")
When a function returns a Result we can pattern match on it to handle success and failure:
case parse_int("123") {
Error(e) -> io.println("That wasn't an Int")
Ok(i) -> io.println("We parsed the Int")
}
This works but if you need to handle multiple Result values in the same function then it becomes verbose, and the error handling obscures the business logic.
case parse_int(a) {
Error(e) -> Error(e)
Ok(int_a) -> case parse_int(b) {
Error(e) -> Error(e)
Ok(int_b) -> Ok(int_a) -> case parse_int(c) {
Error(e) -> Error(e)
Ok(int_c) -> Ok(int_a + int_b + int_c)
}
}
}
With this release Gleam now has the try
keyword which makes error handling
easier and more concise. Rewritten using try
the previous example looks like
this:
try int_a = parse_int(a)
try int_b = parse_int(b)
try int_c = parse_int(c)
Ok(int_a + int_b + int_c)
When a variable is declared using try
Gleam checks to see whether the value
is an Error or an Ok record. If it’s an Ok then the inner value is assigned to
the variable:
try x = Ok(1)
Ok(x + 1)
// -> Ok(2)
If it’s an Error then the Error is returned immediately:
try x = Error("failure")
Ok(x + 1)
// -> Error("failure")
We’ve been testing try
in our applications for a while and we’re very happy
with how it cleans up error handling. Why not give it a try? 😜
Opaque types
Gleam’s new opaque type feature allows a library writer to export a custom type without exporting the internal details of the type.
// in src/box.gleam
pub opaque type Box {
Box(inner_value: Int)
}
pub fn new(x: Int) -> Box {
Box(inner_value: x)
}
If another module imports this custom type they will be able to create a Box
using the new
function, but they cannot call the Box
constructor, use the
.inner_value
accessor, or pattern match on a Box to get the inner value.
import box
let one = box.new(1) // This is permitted
one.inner_value // Compile error!
let box.Box(i) = one // Compile error!
box.Box(1) // Compile error!
This feature is great for creating a carefully controlled API for your data types, hiding any private implementation details to ensure that they are used correctly, which brings us to the next features:
Standard library additions
The standard library has gained a wealth of new modules, types, and functions. Here’s some of the highlights, some of which were made possible with the new opaque type feature.
Iterator
The new Iterator type is a lazily evaluated sequence of elements, similar to the Stream type in Elixir.
Iterators are useful when working with collections that are too large to fit in memory (or those that are infinite in size) as they only require the elements currently being processed to be in memory.
Here we can see the Iterator type used to create an infinitely long sequence of Ints, which is then mapped over, and the first 5 elements are evaluated.
import gleam/iterator
let double = fn(x) { x * 2 }
[1, 2, 3]
|> iterator.from_list // Create a 3 element iterator
|> iterator.cycle // Repeat the iterator forever
|> iterator.map(double) // Double each element
|> iterator.take(5) // Evaluate the first 5 elements
// -> [2, 4, 6, 2, 4]
Because Iterators are lazy even though this Iterator has infinite size the
double
function is only called 5 times.
For more information see the iterator documentation.
Set
A Set is a collection value. Unlike a List, it is unordered, cannot contain duplicate values, and finding a value in a Set is very fast.
import gleam/set
let pop_band = set.new(["Iris", "Karl", "Priya"])
set.contains(pop_band, "Iris")
// -> True
set.contains(pop_band, "Martin")
// -> False
For more information see the set documentation.
Queue
A Queue is an ordered collection of elements. It’s similar to a List, however it’s fast to add and remove from both ends of a queue, while a list can only be quickly accessed from the front.
import gleam/queue
queue.new()
|> queue.push_front(1)
|> queue.push_front(2)
|> queue.push_back(3)
|> queue.push_back(4)
|> queue.to_list
// -> [2, 1, 3, 4]
For more information see the queue documentation.
Option
Option is a type used to represent values which may be present or absent, making it similar to nullable values in other languages. It’s defined like so:
pub type Option(a) {
Some(a)
None
}
If an optional Int “some_int
” is present it’s represented as
Some(the_int)
, while if it’s absent it’s None
.
For more information see the option documentation.
The rest
Other additions to the standard library include an io
module, additions to
the result
, dynamic
, list
, and string
modules, and some changes to
labels to make them more consistent. For full details see the
changelog.
Other additions to the compiler include improvements to the formatter style,
the ability to include additional markdown pages in generated code
documentation, initial support for Elixir’s mix
build tool, type annotations
on let
+ assert
+ try
, custom type support in case clause guards, and
several bug fixes. For more info see the changelog
Thanks
It would not have been possible to have a release this big without the support of all Gleam’s sponsors and contributors, so a huge thank you to them:
- Al Dee
- Arian Daneshvar
- Arno Dirlam
- Bruno Dusausoy
- Bruno Michel
- Bryan Paxton
- BSKY
- Chris Young
- Christian Wesselhoeft
- Clever Bunny LTD
- David cao
- Erik Terpstra
- Fernando Briano
- Hasan YILDIZ
- Hécate
- John Palgut
- José Valim
- Kasim Tuman
- kyle-sammons
- Lars Wikman
- Michael Borohovski
- ontofractal
- Parker Selbert
- Peter Saxton
- Quinn Wilton
- Regan Heath
- Sasan Hezarkhani
- Sascha Wolf
- Saša Jurić
- Sebastian Porto
- Simone Vittori
- Sylvan Morris
- Tom Whatmore
- Tyler Wilcock
If you would like to help make strongly typed programming on the Erlang virtual machine a production-ready reality please consider sponsoring Gleam via the GitHub Sponsors program.
Thanks for reading! Have fun! 💜