Functions in Gleam
Learn how to define and use functions in Gleam - parameters, labelled arguments, anonymous functions, higher-order functions, and recursion with Docker-ready examples
Functions are the heart of Gleam. As a functional language, Gleam treats functions as first-class values: you can store them in variables, pass them as arguments, and return them from other functions. There are no methods, no classes, and no objects - just functions operating on immutable data.
Gleam’s functions are also where its type system shines. Every parameter and return value has a type, but thanks to type inference you rarely need to write those types out by hand. When you do annotate them, the annotations double as documentation that the compiler verifies. And because Gleam has no return keyword and no mutable state, functions are pure expressions that always produce a value.
In this tutorial you’ll learn how to define and call functions, use Gleam’s distinctive labelled arguments, work with anonymous functions and closures, write higher-order functions, and use recursion - which replaces the loops you’d reach for in imperative languages.
Defining and Calling Functions
A function is defined with the fn keyword. Add pub to make it visible outside the module. Parameters and the return value can be annotated with types, but the compiler infers them when you leave them off. There is no return statement - the value of the last expression is what the function returns.
Create a file named functions_basic.gleam:
| |
A few things to notice:
pub fnvsfn-pubexposes the function to other modules; a plainfnis module-private.->declares the return type. Functions defined later in the file (likesquare) can be called from earlier ones - order doesn’t matter within a module.<>is the string concatenation operator, andint.to_stringconverts anIntto aStringfor printing.
This program prints:
3 + 4 = 7
double(10) = 20
square(5) = 25
Labelled Arguments
Gleam lets you give parameters labels so call sites are self-documenting. A labelled parameter has two parts: the label used by callers and the internal name used inside the function body. Labelled arguments can also be supplied in any order, which removes the “which argument was which?” guesswork common to languages with positional-only parameters.
Create a file named functions_labels.gleam:
| |
Both calls produce the same result because the labels - not the position - decide which value goes where:
7
7
Labelled arguments are Gleam’s answer to named and keyword parameters. The standard library uses them heavily (for example string.replace(in:, each:, with:)) to make code read naturally.
Anonymous Functions, Closures, and Function Capture
Functions are values, so you can create them inline without naming them. An anonymous function uses fn(...) { ... }. When it references variables from the surrounding scope, it becomes a closure that captures them. Gleam also offers a shorthand called function capture: writing _ in place of an argument turns a call into a new function.
Create a file named functions_higher_order.gleam:
| |
Walking through main:
apply_twice(increment, 5)appliesincrementtwice:5 -> 6 -> 7.scalecloses overfactor, soscale(10)is10 * 3 = 30.add(2, _)captures the first argument, producing a function whereadd_two(8)is2 + 8 = 10.list.mapapplies the anonymous function to every element, giving[2, 4, 6], andint.sumadds them to12.
7
30
10
12
The type fn(Int) -> Int in apply_twice is a function type - it describes a function taking an Int and returning an Int. This is what makes higher-order functions type-safe.
Recursion
Gleam has no for or while loops. Instead, repetition is expressed with recursion: a function that calls itself. Pattern matching with case defines the base case (when to stop) and the recursive case (how to break the problem down). Because Gleam runs on the BEAM, tail-recursive functions - where the recursive call is the last thing the function does - are optimized into iteration and won’t grow the stack.
Create a file named functions_recursion.gleam:
| |
The sum_list function matches on the shape of the list: [] is the empty list (base case), and [first, ..rest] destructures the head element and the remaining tail. Each call passes a running total forward as an accumulator.
5! = 120
sum = 15
A Comprehensive Example
This program brings the concepts together: a basic function, labelled arguments, an anonymous function passed to a higher-order function, and the pipe operator chaining calls.
Create a file named functions.gleam:
| |
The pipe operator (|>) passes the result of each expression as the first argument to the next, so the final block reads as “take this list, keep the even numbers, then sum them.”
Running with Docker
Gleam uses a project-based build system, so the command below creates a throwaway project, copies your source file into it, and runs it - all in one step. Use the exact image from the language overview.
| |
To run any of the other examples, swap functions.gleam for that file’s name (for example functions_recursion.gleam).
Expected Output
Running functions.gleam produces:
add(2, 3) = 5
2^10 = 1024
tripled sum = 18
even sum = 6
Key Concepts
- Functions are first-class values - store them in variables, pass them as arguments, and return them, all checked by the type system using function types like
fn(Int) -> Int. - No
returnkeyword - a function returns the value of its last expression, keeping functions as pure expressions. - Type inference - parameter and return type annotations are optional; the compiler infers them, but annotations serve as verified documentation.
- Labelled arguments - parameters can have a caller-facing label and an internal name, making call sites self-documenting and order-independent.
- Closures and function capture - anonymous functions capture surrounding variables, and the
_shorthand turns a partial call into a new function. - Higher-order functions - functions that take or return other functions power standard-library tools like
list.mapandlist.filter. - Recursion replaces loops - with no
fororwhile, repetition is expressed through recursion and pattern matching; tail-recursive functions are optimized by the BEAM. - The pipe operator (
|>) - chains function calls into readable top-to-bottom data transformations.
Running Today
All examples can be run using Docker:
docker pull ghcr.io/gleam-lang/gleam:v1.14.0-erlang-alpine
Comments
Loading comments...
Leave a Comment