Gleam is a statically typed functional programming language for building scalable concurrent systems.

It compiles to Erlang and has straightforward interop with other BEAM languages such as Erlang, Elixir and LFE.

It looks like this:

pub enum Tree(value) =
  | Leaf(value)
  | Node(Tree(value), Tree(value))

pub fn any(tree: Tree(a), check: fn(a) -> Bool) -> Bool {
  case tree {
  | Leaf(i) -> check(i)
  | Node(left, right) -> any(left, check) || any(right, check)

pub fn has_even_leaf(tree: Tree(Int)) -> Bool {
  any(tree, fn(i) {
    i % 2 == 0

The source code can be found at

For Gleam chat we have the IRC channel #gleam-lang on Freenode.


Be safe

An expressive type system inspired by the ML family of languages helps us find and prevent bugs at compile time, long before it reaches your users.

For the problems the type system can't solve (such as your server being hit by a bolt of lightning) the Erlang/OTP runtime provides well tested mechanisms for gracefully handling failure.

Be friendly

Hunting down bugs can be stressful so feedback from the compiler should be as clear and helpful as possible. We want to spend more time working on our application and less time looking for typos or deciphering cryptic error messages.

As a community we want to be friendly too. People of all backgrounds, genders, and experience levels are welcome and must receive equal respect.

Be performant

The Erlang/OTP runtime is known for its speed and ability to scale, enabling organisations such as WhatsApp and Ericsson to reliably handle massive amounts of traffic at low latency. Gleam should take full advantage of this runtime and be as fast as other BEAM languages such as Erlang and Elixir.

Be a good citizen

Gleam makes it easy to use code written in other BEAM languages such as Erlang, Elixir and LFE, so there's a rich ecosystem of tools and library for Gleam users to make use of.

Users of other BEAM languages should in return be able to take advantage of Gleam, either by transparently making use of libraries written in Gleam, or by adding Gleam modules to their existing project with minimal fuss.

Getting started

In this chapter we get the Gleam language set up on your computer and learn how to create an navigate a Gleam project.

Good luck, have fun!

Installing Gleam

Precompiled for Linux or macOS

The easiest way to install Gleam on Linux and Apple macOS is to download a prebuilt version of the compiler from the GitHub release page.

asdf version manager

asdf is a tool for installing and managing multiple version of programming languages at the same time. Install the asdf-gleam plugin to manage Gleam with asdf.

Arch Linux

Gleam is available through the Arch User Repository as package gleam. You can use your prefered helper to install it or clone it for manual build from

Build from source

The compiler is written in the Rust programming language and so if you wish to build Gleam from source you will need to install the Rust compiler.

# Download the Gleam source code git repository
cd /tmp
git clone --branch v0.2.0
cd gleam

# Build the Gleam compiler. This will take some time!
make install

# Verify the compiler is installed
# Prints "gleam $VERSION"
gleam --version

Installing Erlang

Gleam compiles to Erlang code, so Erlang needs to be installed to run Gleam code.

Precompiled builds for many popular operating systems can be downloaded from the Erlang solutions website,

Guides for installing Erlang on specific operating systems can be found below, as well as information on installing multiple versions of Erlang at once using version manager tools.

Once Erlang has been installed you can check it is working by typing erl -version in your computer's terminal. You will see version information like this if all is well:

$ erl -version
Erlang (SMP,ASYNC_THREADS,HIPE) (BEAM) emulator version 10.1


Debian Linux

sudo apt-get update
sudo apt-get install erlang

Ubuntu Linux

sudo apt-get update
sudo apt-get install erlang

Mac OS X

Using Homebrew

With Homebrew installed run the following:

brew update
brew install erlang


Using Chocolatey

With Chocolatey installed on your computer run the following:

choco install erlang

Using version managers


The asdf version manager has a plugin for installing Erlang. Installation and usage instructions can be found here:

Editor support

Gleam plugins are available for several popular editors. If one exists for your editor of choice consider installing it for syntax highlighting and other niceties.

Creating a project

Installing the rebar3 build tool

Note: Gleam's tooling is very young and in a state of flux. Expect rough edges and breaking changes to come.

The Gleam compiler can build Gleam projects that are managed with the standard Erlang build tool, rebar3. If you don't have rebar3 installed please install it now.

Generating a project

Now a project can be generated like so:

gleam new my_fantastic_project
cd my_fantastic_project

You'll now have a project with this structure:

├── gleam.toml
├── rebar.config
├── src
│   ├──
│   └── my_fantastic_project.gleam
└── test
    └── my_fantastic_project_test.gleam

2 directories, 7 files

The project is managed and built using rebar3, the standard Erlang build tool. Here are some commonly used commands rebar3 commands that you can use with your new project:

# Run an interactive shell with your code loaded (Erlang syntax)
rebar3 shell

# Run the eunit tests
rebar3 eunit

More information can be found on the rebar3 documentation website.

What next?

Want to see some Gleam code? See the example projects.

Looking to learn the language? Check out the language tour.

Need ideas for a project? We have a list of libraries that need writing.

Example projects

When learning a new language it can often be used to have example code to refer to and learn from, so here's some examples:

Tiny: URL shortener

A simple HTML serving web application that takes URLs and gives the user a shorter URL to use in its place.

Uses the Elli web server and the Postgresql database via the epgsql client.

The Gleam standard library

A collection of modules for working with the common data structures of Gleam. Makes heavy use of Erlang FFI.

Language Tour

In this chapter we explore the fundamentals of the Gleam language, namely its syntax, core data structures, flow control features, and static type system.

After completion the reader should know enough to start reading and writing Gleam code, assuming they have some prior programming experience.

In some section we touch on the runtime representation of various features. This is useful for programmers with Erlang or Elixir experience who wish to use Gleam alongside these languages. If you are using Gleam alone this information can be safely ignored.


Gleam allows you to write comments in your code.

Here’s a simple comment:

// Hello, world!

In Gleam, comments must start with two slashes and continue until the end of the line. For comments that extend beyond a single line, you’ll need to include // on each line, like this:

// Hello, world! I have a lot to say, so much that it will take multiple
// lines of text. Therefore, I will start each line with // to denote it
// as part of a multi-line comment.

Comments can also be placed at the end of lines containing code:

pub fn add(x, y) {
  x + y // here we are adding two values together

Comments may also be indented:

pub fn multiply(x, y) {
  // here we are multiplying x by y
  x * y 


Gleam's has UTF-8 binary strings, written as text surrounded by double quotes.

"Hello, Gleam!"

Strings can span multiple lines.


Special characters such as " need to be escaped with a \ character.

"Here is a double quote -> \" <-"


A Bool can be either True or False.

Gleam defines a handful of operators that work with Bools.

False && False // => False
False && True  // => False
True && False  // => False
True && True   // => True

False || False // => False
False || True  // => True
True || False  // => True
True || True   // => True

&& and || are short circuiting, meaning they don't evaluate the right hand side if they don't have to.

&& evaluates the right hand side if the left hand side is True.

|| evaluates the right hand side if the left hand side is False.

Erlang interop

While written in the code using a capital letter, they are represented at runtime with the atoms true and false, making them compatible with Elixir and Erlang's booleans.

This is important if you want to use Gleam and Elixir or Erlang together in one project.

// Gleam
% Erlang

Int and Float

Gleam's main number types are Int and Float.


Ints are "whole" numbers.


Gleam has several operators that work with Ints.

1 + 1 // => 2
5 - 1 // => 4
5 / 2 // => 2
3 * 3 // => 9
5 % 2 // => 1

2 > 1  // => True
2 < 1  // => False
2 >= 1 // => True
2 <= 1 // => False


Floats are numbers that have a decimal point.


Floats also have their own set of operators.

1.0 +. 1.4 // => 2.4
5.0 -. 1.5 // => 3.5
5.0 /. 2.0 // => 2.5
3.0 *. 3.1 // => 9.3

2.0 >. 1.0  // => True
2.0 <. 1.0  // => False
2.0 >=. 1.0 // => True
2.0 <=. 1.0 // => False

Let bindings

A value can be given a name using let. Names can be reused by later let bindings, but the values contained are immutable, meaning the values themselves cannot be changed.

let x = 1
let y = x
let x = 2

x  // => 2
y  // => 1


Lists are ordered collections of values. They're one of the most common data structures in Gleam.

Lists are homogeneous, meaning all the elements of a List must be of the same type. Attempting to construct a list of multiple types of element will result in the compiler presenting a type error.

[1, 2, 3, 4]  // List(Int)
[1.22, 2.30]  // List(Float)
[1.22, 3, 4]  // Type error!

Prepending to a list is very fast, and is the preferred way to add new values.

[1 | [2, 3]]  // => [1, 2, 3]

Note that as all data structures in Gleam are immutable so prepending to a list does not change the original list, instead it efficiently creates a new list with the new additional element.

let x = [2, 3]
let y = [1 | x]

x  // => [2, 3]
y  // => [1, 2, 3]


The case expression is the most common kind of flow control in Gleam code. It allows us to say "if the data has this shape then do that", which we call pattern matching.

Here we match on an Int and return a specific string for the values 0, 1, and 2. The final pattern n matches any other value that did not match any of the previous patterns.

case some_number {
| 0 -> "Zero"
| 1 -> "One"
| 2 -> "Two"
| n -> "Some other number" // This matches anything

Pattern matching on a Bool value is the Gleam alternative to the if else statement found in other languages.

case some_bool {
| True -> "It's true!"
| False -> "It's not true."

Gleam's case is an expression, meaning it returns a value and can be used anywhere we would use a value. For example, we can name the value of a case expression with a let binding.

let description =
  case True {
  | True -> "It's true!"
  | False -> "It's not true."

description  // => "It's true!"


A case expression can be used to destructure values that contain other values, such as tuples and lists.

case xs {
| [] -> "This list is empty"
| [a] -> "This list has 1 element"
| [a, b] -> "This list has 2 element"
| other -> "This list has more than 2 elements"

It's not just the top level data structure that can be pattern matches, contained values can also be matched. This gives case the ability to concisely express flow control that might be verbose without pattern matching.

case xs {
| [[]] -> "The only element is an empty list"
| [[] | _] -> "The 1st element is an empty list"
| [[4] | _] -> "The 1st element is a list of the number 4"
| other -> "Something else"

Pattern matching also works in let bindings, though patterns that do not match all instances of that type may result in a runtime error.

let [a] = [1]    // a is 1
let [b] = [1, 2] // Runtime error! The pattern has 1 element but the value has 2


Named functions

Named functions in Gleam are defined using the pub fn keywords.

pub fn add(x, y) {
  x + y

pub fn multiply(x, y) {
  x * y

Functions in Gleam are first class values and so can be assigned to variables, passed to functions, or anything else you might do with any other data type.

// This function takes a function as an argument
pub fn twice(f, x) {

pub fn add_one(x) {
  x + 1

pub fn add_two(x) {
  twice(add_one, x)

Type annotations

Function arguments can be optionally be annotated with their type. The compiler will check these annotations and ensure they are correct.

fn identity(x: Int) -> Int {

Without an annotation this identity function would have have the inferred type fn(a) -> a, but the type annotation on the argument results in the type of the function being fn(Int) -> Int. This shows how type annotations can be used to create functions with types less general than the compiler may have inferred otherwise.

Anonymous functions

Anonymous functions can be defined with a similar syntax.

pub fn run() {
  let add = fn(x, y) { x + y }

  add(1, 2)

Function capturing

There is a shorthand syntax for creating anonymous functions that take one argument and call another function. The _ is used to indicate where the argument should be passed.

pub fn add(x, y) {
  x + y

pub fn run() {
  let add_one = add(1, _)


The function capture syntax is often used with the pipe operator to create a series of transformations on some data.

pub fn add(x, y) {
  x + y

pub fn run() {
  // This is the same as add(add(add(1, 3), 6), 9)
  |> add(_, 3)
  |> add(_, 6)
  |> add(_, 9)


Gleam programs are made up of bundles of functions and types called modules. Each module has its own namespace and can export types and values to be used by other modules in the program.

// inside src/nasa/rocket_ship.gleam

fn count_down() {
  "3... 2... 1..."

fn blast_off() {

pub fn launch() {

Here we can see a module named nasa/rocket_ship, the name determined by the filename src/nasa/rocket_ship.gleam. Typically all the modules for one project would live within a directory with the name of the project, such as nasa in this example.

For the functions count_down and blast_off we have omitted the pub keyword, so these functions are private module functions. They can only be called by other functions within the same module.


To use functions or types from another module we need to import them using the import keyword.

// inside src/nasa/moon_base.gleam

import nasa/rocket_ship

pub fn explore_space() {

The statement import nasa/rocket_ship creates a new variable with the name rocket_ship and the value of the rocket_ship module.

In the explore_space function we call the imported module's public launch function using the . operator. If we had attempted to call count_down it would result in a compile time error as this function is private in the rocket_ship module.

Named import

It is also possible to give a module a custom name when importing it using the as keyword.

import unix/cat
import animal/cat as kitty

This may be useful to differentiate between multiple modules that would have the same default name when imported.


Gleam's struct types are named collections of keys and values. They are similar to objects in object oriented languages, though they don't have methods.

Structs are defined with the struct keyword.

pub struct Cat {
  name: String
  cuteness: Int

Here we have defined a struct called Cat which has two fields: A name field which is an Int, and a cuteness field which is an Int.

The pub keyword has been used to make this struct usable from other modules.

Once defined the struct type can be used in functions:

fn cats() {
  // Struct fields can be given in any order
  let cat1 = Cat(name: "Nubi", cuteness: 2001)
  let cat2 = Cat(cuteness: 1805, name: "Biffy")

  // Alternatively fields can be given without labels
  let cat3 = Cat("Ginny", 1950)

  [cat1, cat2, cat3]

Destructuring structs

To extract values from structs we can pattern match on them with let or case.

let Cat(name: name1, cuteness: cuteness1) = cat1

name1     // => "Nubi"
cuteness1 // => 2001

We can also pattern match using positional arguments:

let Cat(name2, cuteness2) = cat2

name2     // => "Biffy"
cuteness2 // => 1805

Generic structs

Structs types can be parameterised so the same struct can be constructed with different contained types.

pub struct Box(a) {
  tag: String,
  contents: a, // The type of this field is injected when constructed

fn run() {
  Box(tag: "one", contents: 1.0) // the type is Box(Float)
  Box(tag: "two", contents: "2") // the type is Box(String)

Commonly used structs

Pair(a, b)

There may be times when you want to move two values together, or return them both from a function. To save you from having to define a new struct for this the Gleam standard library implements a Pair type in the gleam/pair module which can contain any two values.

import gleam/pair

fn run() {
  pair.Pair("ok", 100) // type is Pair(String, Int)
  pair.Pair(1.01, [1]) // type is Pair(Float, List(Int))

Erlang interop

At runtime Gleam structs are Erlang tuples. All information about the names of the keys is erased at runtime.


Enums in Gleam are a way of modeling data that can be one of a few different variants. They must be declared before use, and the names of variants must be unique for the given module.

We've seen an enum already in this chapter- Bool.

Bool is defined like this:

// A Bool is a value that is either `True` or `False`
enum Bool =
  | True
  | False

Enum variants can also contain other values, and these values can be extracted using a let binding.

enum User =
  | LoggedIn(String)  // A logged in user with a name
  | Guest             // A guest user with no details
let sara = LoggedIn("Sara")
let rick = LoggedIn("Rick")
let visitor = Guest


When given an enum we can pattern match on it to determine which variant it is and to assign names to any contained values.

fn get_name(user) {
  case user {
  | LoggedIn(name) -> name
  | Guest -> "Guest user"

Enums can also be destructured with a let binding.

enum Score =
  | Points(Int)
let score = Points(50)
let Points(p) = score

p // => 50

Commonly used enums


pub enum Bool =
  | True
  | False

As seen above Gleam's Bool type is an enum! Use it to answer yes/no questions and to indicate whether something is True or False.

Result(value, error)

pub enum Result(value, reason) =
  | Ok(value)
  | Error(reason)

Gleam doesn't have exceptions of null to represent errors in our programs, instead we have the Result type. If a function call fail wrap the returned value in a Result, either Ok if the function was successful, or Error if it failed.

pub fn lookup(name, phone_book) {
  // ... we found a phone number in the phone book for the given name here

The Error type needs to be given a reason for the failure in order to return, like so:

pub enum MyDatabaseError =
  | InvalidQuery
  | NetworkTimeout

pub fn insert(db_row) {
  // ... something went wrong connecting to a database here

In cases where we don't care about the specific error enough to want to create a custom error type, or when the cause of the error is obvious without further detail, the Nil type can be used as the Error reason.

pub fn lookup(name, phone_book) {
  // ... That name wasn't found in the phone book

When we have a Result type returned to us from a function we can pattern match on it using case to determine whether we have an Ok result or an Error result.

The standard library gleam/result module contains helpful functions for working with the Result type, make good use of them!

Erlang interop

At runtime enum variants with no contained values become atoms. The atoms are written in snake_case rather than CamelCase so LoggedIn becomes logged_in.

Enum variants with contained values become tuples with a tag atom.

// Gleam
# Elixir
{:logged_in, "Kim"}
% Erlang
{logged_in, <<"Kim">>}.

External function

Gleam is just one of many languages on the Erlang virtual machine and at times we may want to use functions from these other languages in our Gleam programs. To enable this Gleam allows the importing of external functions, which may be written in any BEAM language.

External functions are typically written in a different language with a different type system, so the compiler is unable to determine the type of the function and instead the programmer must inform the compiler the type.

Gleam trusts that the type given is correct so an inaccurate type annotation can result in unexpected behaviour and crashes at runtime. Be careful!


The Erlang rand module has a function named uniform that takes no arguments and returns a Float.

The Elixir module IO has a function named inspect that takes any value, prints it, and returns the same value.

If we want to import these functions and use them in our program we would do so like this:

pub external fn random_float() -> Float = "rand" "uniform"

// Elixir modules start with `Elixir.`
pub external fn inspect(a) -> a = "Elixir.IO" "inspect"

External type

In addition to importing external functions we can also import external types. Gleam knows nothing about the runtime representation of these types and so they cannot be pattern matched on, but they can be used with external functions that know how to work with them.

Here is an example of importing a Queue data type and some functions from Erlang's queue module to work with the new Queue type.

pub external type Queue(a)

pub external fn new() -> Queue(a) = "queue" "new"

pub external fn length(Queue(a)) -> Int = "queue" "len"

pub external fn push(Queue(a), a) -> Queue(a) = "queue" "in"


Why is the compiler written in Rust?

Prototype versions of the Gleam compiler was written in Erlang, but a switch was made to Rust as the lack of static types was making refactoring a slow and error prone process. A full Rust rewrite of the prototype resulted in the removal of a lot of tech debt and bugs, and the performance boost is nice too!

One day Gleam may have a compiler written in Gleam, but for now we are focused on developing other areas of the language such as libraries, tooling, and documentation.

Will Gleam have type classes?

Some form of ad-hoc polymorphism could be a good addition to the ergonomics of the language, though what shape that may take is unclear. Type classes are one option, OCaml style implicit modules are another, or perhaps it'll be something else entirely.

How is message passing typed?

Gleam doesn't currently have first class support for the BEAM's concurrency primitives such as receive, send, and spawn. This is because research is still ongoing as to the best way to apply a strong type system to them while still enabling established OTP patterns. For now these primitives should be used via the Erlang FFI, making them dynamically typed.

Many OTP patterns such as gen_server are functional in nature and don't require direct use of these primitives so these behaviours can be implemented in Gleam today.

How does Gleam compare to Alpaca?

Alpaca is similar to Gleam in that it is a statically typed language for the Erlang VM that is inspired by the ML family of languages. It's a wonderful project and we hope they are wildly successful!

Here's a non-exhaustive list of differences:

  • Alpaca functions are auto-curried, Gleam's are not.
  • Alpaca's unions can be untagged, with Gleam all variants in an enum need a name.
  • Alpaca's compiler is written in Erlang, Gleam's is written in Rust.
  • Alpaca's syntax is closer to ML family languages, Gleam's is closer to C family languages.
  • Alpaca compiles to Core Erlang, Gleam compiles to regular Erlang.

Alpaca is great, check it out! :)

Should I put Gleam in production?

Probably not. Gleam is a very young language and there may be all kinds of problems and breaking changes down the line.

Having said that, the Erlang VM is extremely mature and well tested, and if you decide to move away from Gleam the language you can compile your code to Erlang and maintain that in future.

Is it good?

Yes, I think so. :)