コンテンツにスキップ

Gleam Cheat Sheet

Overview

Gleam is a friendly language for building type-safe, scalable systems. It compiles to Erlang and JavaScript, letting you build for the BEAM (Erlang VM) or the browser and Node.js. Gleam brings static typing to the BEAM ecosystem while maintaining full interoperability with Erlang and Elixir libraries. The language features algebraic data types, pattern matching, generics, and a powerful type inference system that catches errors at compile time.

Created by Louis Pilfold, Gleam reached version 1.0 in March 2024. It combines the rock-solid reliability of the Erlang VM (known for powering telecom systems with 99.9999999% uptime) with a modern type system and developer experience. Gleam’s compiler provides helpful error messages, the language has no null values or exceptions, and its module system encourages clean, maintainable code. It integrates seamlessly with OTP patterns for building concurrent, fault-tolerant applications.

Installation

Package Managers

# macOS
brew install gleam

# Linux (asdf)
asdf plugin add gleam
asdf install gleam latest
asdf global gleam latest

# Linux (Nix)
nix-shell -p gleam

# Windows (scoop)
scoop install gleam

Prerequisites

# Erlang is required for BEAM target
brew install erlang      # macOS
sudo apt install erlang  # Ubuntu

# Node.js for JavaScript target (optional)
brew install node

Create Project

# New project
gleam new my_app
cd my_app

# Build and run
gleam run

# Run tests
gleam test

# Build only
gleam build

# Format code
gleam format

# Add dependency
gleam add gleam_json
gleam add gleam_http

Core Language

Types and Variables

// Let bindings (immutable)
let name = "Gleam"
let age: Int = 42
let pi: Float = 3.14159

// Type annotations are optional (inferred)
let greeting = "Hello, " <> name <> "!"

Basic Types

TypeExampleDescription
Int42, 0xFF, 1_000Arbitrary precision integer
Float3.14, 1.5e364-bit floating point
String"hello"UTF-8 string
BoolTrue, FalseBoolean
NilNilUnit type
List(a)[1, 2, 3]Linked list
#(a, b)#(1, "hi")Tuple
Result(a, e)Ok(42), Error("fail")Success or failure
Option(a)Some(42), NoneOptional value
BitArray<<1, 2, 3>>Binary data

Functions

// Named function
fn add(a: Int, b: Int) -> Int {
  a + b
}

// Public function
pub fn greet(name: String) -> String {
  "Hello, " <> name <> "!"
}

// Anonymous function
let double = fn(x: Int) -> Int { x * 2 }

// Function with labeled arguments
pub fn connect(to host: String, on port: Int) -> Connection {
  // host and port are used inside
  // called as: connect(to: "localhost", on: 5432)
}

// Pipeline operator
"hello world"
|> string.uppercase
|> string.split(" ")
|> list.first
// => Ok("HELLO")

// Higher-order functions
let numbers = [1, 2, 3, 4, 5]
let doubled = list.map(numbers, fn(x) { x * 2 })
let evens = list.filter(numbers, fn(x) { x % 2 == 0 })
let sum = list.fold(numbers, 0, fn(acc, x) { acc + x })

Custom Types (Algebraic Data Types)

// Record type
pub type User {
  User(name: String, email: String, age: Int)
}

// Create
let alice = User(name: "Alice", email: "alice@example.com", age: 30)

// Access fields
let name = alice.name

// Update (creates new value)
let updated = User(..alice, age: 31)

// Sum type (variants)
pub type Shape {
  Circle(radius: Float)
  Rectangle(width: Float, height: Float)
  Triangle(base: Float, height: Float)
}

pub fn area(shape: Shape) -> Float {
  case shape {
    Circle(radius:) -> 3.14159 *. radius *. radius
    Rectangle(width:, height:) -> width *. height
    Triangle(base:, height:) -> base *. height /. 2.0
  }
}

// Generic types
pub type Pair(a, b) {
  Pair(first: a, second: b)
}

// Option type
pub type Option(a) {
  Some(a)
  None
}

Pattern Matching

// Case expression
pub fn describe(x: Int) -> String {
  case x {
    0 -> "zero"
    1 -> "one"
    n if n > 0 -> "positive"
    _ -> "negative"
  }
}

// Matching on custom types
pub fn to_string(result: Result(Int, String)) -> String {
  case result {
    Ok(value) -> "Success: " <> int.to_string(value)
    Error(message) -> "Error: " <> message
  }
}

// Multiple values
case x, y {
  0, 0 -> "origin"
  0, _ -> "y-axis"
  _, 0 -> "x-axis"
  _, _ -> "other"
}

// List patterns
case my_list {
  [] -> "empty"
  [only] -> "one element: " <> only
  [first, ..rest] -> "starts with: " <> first
}

// Let assert (crash on no match)
let assert Ok(value) = get_data()

Error Handling with Result

import gleam/result

pub fn parse_and_double(input: String) -> Result(Int, String) {
  // use keyword for early return on error (like ? in Rust)
  use number <- result.try(int.parse(input))
  use validated <- result.try(validate(number))
  Ok(validated * 2)
}

// Result chaining
let output =
  input
  |> int.parse
  |> result.map(fn(n) { n * 2 })
  |> result.unwrap(0)

// result.try for monadic chaining
pub fn process() -> Result(String, Error) {
  use user <- result.try(fetch_user(id))
  use profile <- result.try(fetch_profile(user))
  Ok(profile.name)
}

Lists and Iterators

import gleam/list
import gleam/iterator

// List operations
let nums = [3, 1, 4, 1, 5, 9]
list.map(nums, fn(x) { x * 2 })        // [6, 2, 8, 2, 10, 18]
list.filter(nums, fn(x) { x > 3 })     // [4, 5, 9]
list.fold(nums, 0, fn(acc, x) { acc + x }) // 23
list.sort(nums, int.compare)             // [1, 1, 3, 4, 5, 9]
list.unique(nums)                        // [3, 1, 4, 5, 9]
list.flat_map(nums, fn(x) { [x, x] })  // [3, 3, 1, 1, ...]
list.chunk(nums, fn(x) { x > 3 })      // [[3, 1], [4], [1], [5, 9]]
list.zip(["a", "b"], [1, 2])            // [#("a", 1), #("b", 2)]

// Lazy iterators
iterator.range(1, 1000)
|> iterator.filter(fn(x) { x % 2 == 0 })
|> iterator.map(fn(x) { x * x })
|> iterator.take(5)
|> iterator.to_list

Modules and Imports

// src/my_app/user.gleam
import gleam/int
import gleam/string
import gleam/option.{type Option, None, Some}

// Qualified import
import gleam/list

// Aliased import
import gleam/io as console

// Type-only import
import my_app/types.{type User}

// Public types and functions are exported automatically
pub type Role {
  Admin
  Member
  Guest
}

pub fn is_admin(role: Role) -> Bool {
  role == Admin
}

Build and Dependencies

gleam.toml

name = "my_app"
version = "1.0.0"
target = "erlang"  # or "javascript"

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
gleam_json = ">= 1.0.0 and < 2.0.0"
gleam_http = ">= 3.5.0 and < 4.0.0"
gleam_otp = ">= 0.10.0 and < 1.0.0"
mist = ">= 1.0.0 and < 2.0.0"
wisp = ">= 0.14.0 and < 1.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

Build Commands

CommandDescription
gleam new project_nameCreate new project
gleam buildCompile project
gleam runBuild and run
gleam testRun tests
gleam formatFormat all files
gleam add package_nameAdd dependency
gleam remove package_nameRemove dependency
gleam docs buildGenerate documentation
gleam publishPublish to Hex.pm
gleam export erlang-shipmentCreate release

Configuration

Using Environment and Config

import gleam/erlang/os

pub fn get_port() -> Int {
  os.get_env("PORT")
  |> result.then(int.parse)
  |> result.unwrap(3000)
}

Advanced Usage

OTP Integration (Erlang target)

import gleam/otp/actor
import gleam/erlang/process

// Define actor message type
pub type Message {
  Increment
  GetCount(reply_with: process.Subject(Int))
}

// Start actor
pub fn start() -> Result(process.Subject(Message), actor.StartError) {
  actor.start(0, fn(message, count) {
    case message {
      Increment -> actor.continue(count + 1)
      GetCount(client) -> {
        process.send(client, count)
        actor.continue(count)
      }
    }
  })
}

Web Server with Wisp

import wisp.{type Request, type Response}
import mist

pub fn handle_request(req: Request) -> Response {
  case wisp.path_segments(req) {
    [] -> wisp.ok() |> wisp.string_body("Welcome!")
    ["api", "users"] -> handle_users(req)
    ["api", "users", id] -> handle_user(req, id)
    _ -> wisp.not_found()
  }
}

fn handle_users(req: Request) -> Response {
  case req.method {
    http.Get -> {
      let users = get_all_users()
      wisp.ok() |> wisp.json_body(encode_users(users))
    }
    http.Post -> {
      use body <- wisp.require_json(req)
      // process body
      wisp.created()
    }
    _ -> wisp.method_not_allowed([http.Get, http.Post])
  }
}

pub fn main() {
  wisp.configure_logger()
  let assert Ok(_) =
    handle_request
    |> wisp.mist_handler(secret_key)
    |> mist.new
    |> mist.port(3000)
    |> mist.start_http
  process.sleep_forever()
}

Testing

import gleeunit
import gleeunit/should

pub fn main() {
  gleeunit.main()
}

pub fn add_test() {
  add(2, 3)
  |> should.equal(5)
}

pub fn greet_test() {
  greet("World")
  |> should.equal("Hello, World!")
}

pub fn parse_error_test() {
  int.parse("not a number")
  |> should.be_error
}

Troubleshooting

ProblemSolution
Type mismatchCheck function signatures; Gleam’s type inference is strict
Missing ErlangInstall Erlang/OTP for BEAM target; check erl on PATH
Dependency conflictUpdate gleam.toml version ranges; run gleam deps download
Pattern match not exhaustiveAdd missing cases; use _ for catch-all
let assert crashHandle errors with case or result.try instead
FFI type errorsCheck external function declarations match Erlang/JS types
Build cache issuesDelete build/ directory and rebuild
JavaScript target issuesNot all BEAM libraries work on JS; check package compatibility