Gleam
Friendly, typed and runs on the BEAM.
Gleam is a young, statically typed functional language that runs on the Erlang virtual machine (BEAM) and can also compile to JavaScript. It brings a friendly syntax and a helpful compiler to the fault-tolerant world of Erlang and Elixir, and it reached version 1.0 in 2024.
Why learn Gleam?
Gleam gives you Erlang's reliability with modern types and very readable code.
- Type safe: no null, no exceptions for expected errors, exhaustive matching.
- BEAM power: millions of lightweight processes and supervision.
- Two targets: share code between server (Erlang) and browser (JavaScript).
Set up and run your first program
Install Gleam and Erlang/OTP, then create a project.
gleam new hello_ai
cd hello_ai
gleam runimport gleam/io
pub fn main() {
let name = "Ada"
io.println("Hello, AI! Welcome to Gleam, " <> name <> ".")
}<>. Add packages with gleam add.Values and functions
let binds immutable values. Functions are declared with fn and the last expression is returned. Ints and floats have separate operators (+ and +.).
import gleam/int
import gleam/io
pub fn params(layers: Int, width: Int) -> Int {
12 * layers * width * width
}
pub fn fp16_gb(p: Float) -> Float {
p *. 2.0 /. 1.0e9
}
pub fn main() {
let layers = 32
io.println("Params: " <> int.to_string(params(layers, 4096)))
}Custom types and case
Custom types list every variant. case must handle them all, and can match on values and structure.
pub type Device {
Cpu
Gpu(name: String, vram: Int)
Tpu(version: Int)
}
pub fn describe(d: Device) -> String {
case d {
Cpu -> "CPU"
Gpu(name, vram) if vram >= 80 -> "data center GPU " <> name
Gpu(name, _) -> "GPU " <> name
Tpu(_) -> "TPU"
}
}Lists and pipelines
The gleam/list module covers mapping, filtering and folding. The pipe |> passes values along.
import gleam/io
import gleam/list
import gleam/string
pub fn main() {
let vram = [#("H100", 80), #("H200", 141), #("RTX 5090", 32)]
vram
|> list.filter(fn(p) { p.1 >= 80 })
|> list.map(fn(p) { p.0 })
|> string.join(", ")
|> io.println
let total = list.fold(vram, 0, fn(acc, p) { acc + p.1 })
io.debug(total)
}Results and use
Operations that can fail return Result. The use expression keeps chains of fallible steps flat and readable.
import gleam/int
import gleam/result
pub fn fit(model_gb: Int, vram_gb: Int) -> Result(Int, String) {
case model_gb <= vram_gb {
True -> Ok(vram_gb - model_gb)
False -> Error("model does not fit")
}
}
pub fn free_after(input: String) -> Result(Int, String) {
use gb <- result.try(int.parse(input) |> result.replace_error("not a number"))
fit(gb, 24)
}Processes with OTP
The gleam_otp package gives typed actors on the BEAM. Each actor owns its state and handles messages one at a time.
import gleam/erlang/process
import gleam/otp/actor
pub type Msg {
Add(Int)
Get(reply: process.Subject(Int))
}
fn handle(msg: Msg, total: Int) -> actor.Next(Msg, Int) {
case msg {
Add(n) -> actor.continue(total + n)
Get(reply) -> {
process.send(reply, total)
actor.continue(total)
}
}
}
pub fn main() {
let assert Ok(counter) = actor.start(0, handle)
process.send(counter, Add(5))
let total = process.call(counter, Get, 100)
echo total
}AI with Gleam
Gleam's strengths are reliable, concurrent services, which suits AI backends: routing requests to model servers, streaming chat responses, and handling many users at once. It calls LLM APIs with gleam_httpc and decodes responses with typed decoders, and it can use Elixir and Erlang libraries on the same VM.
The essential libraries
Gradient descent from scratch
Fit y = 3x + 0.5 with lists and folds.
import gleam/int
import gleam/io
import gleam/float
import gleam/list
pub fn main() {
let xs = list.range(0, 99) |> list.map(fn(i) { -1.0 +. 2.0 *. int.to_float(i) /. 99.0 })
let ys = list.map(xs, fn(x) { 3.0 *. x +. 0.5 })
let n = 100.0
let #(w, b) =
list.fold(list.range(1, 500), #(0.0, 0.0), fn(wb, _) {
let #(w, b) = wb
let errs = list.map2(xs, ys, fn(x, y) { w *. x +. b -. y })
let gw = 2.0 *. float.sum(list.map2(errs, xs, fn(e, x) { e *. x })) /. n
let gb = 2.0 *. float.sum(errs) /. n
#(w -. 0.1 *. gw, b -. 0.1 *. gb)
})
io.println("w = " <> float.to_string(w) <> ", b = " <> float.to_string(b))
}Call an LLM from Gleam
With gleam_http, gleam_httpc and gleam_json you can send a chat request and decode the reply into typed values. A tool-calling loop adds a tools field and repeats while tool_calls are returned.
import gleam/dynamic/decode
import gleam/http
import gleam/http/request
import gleam/httpc
import gleam/io
import gleam/json
pub fn main() {
let body =
json.object([
#("model", json.string("llama3.1")),
#("messages", json.preprocessed_array([
json.object([#("role", json.string("user")),
#("content", json.string("Explain tensor cores in one sentence."))]),
])),
])
let assert Ok(req) = request.to("http://localhost:11434/v1/chat/completions")
let req =
req
|> request.set_method(http.Post)
|> request.set_header("content-type", "application/json")
|> request.set_body(json.to_string(body))
let assert Ok(resp) = httpc.send(req)
let content =
decode.at(["choices"], decode.list(decode.at(["message", "content"], decode.string)))
case json.parse(resp.body, content) {
Ok([text, ..]) -> io.println(text)
_ -> io.println("could not decode reply")
}
}Gleam's standard library changes quickly before it settles; check the docs for the versions you install.
ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.Gleam learning roadmap
Tick items off as you go. Your progress is saved in this browser.
Beginner
Intermediate
Advanced
Projects to build
Gleam cheat sheet
| Task | Syntax |
|---|---|
| Value | let x = 5 |
| Function | pub fn add(a: Int, b: Int) -> Int { a + b } |
| String join | "a" <> "b" |
| Pipe | xs |> list.map(f) |
| Case | case x { Ok(v) -> v Error(_) -> 0 } |
| Tuple | #("H100", 80) |
Common mistakes
- Ints and floats use different operators:
+vs+.. - There are no exceptions for normal errors; return and handle Result.
- The language is young, so APIs in packages can change.
Where to go next
- Gleam tour — interactive
- Gleam docs — official
- Gleam packages — libraries
On TensorCore.si: Elixir volume, Erlang volume.
Keyboard: [ previous language · ] next language · / search the library