Gleam Higher-Order Functions
A higher-order function accepts other functions as arguments or returns a function as its result. This approach lets you separate "what to do" from "how to repeat it" — writing flexible, reusable logic without duplication.
Functions as Arguments
pub fn apply(value: Int, operation: fn(Int) -> Int) -> Int {
operation(value)
}
pub fn double(n: Int) -> Int { n * 2 }
pub fn square(n: Int) -> Int { n * n }
pub fn main() {
apply(5, double) // 10
apply(5, square) // 25
}
Higher-Order Function Flow
──────────────────────────────────────────────────
apply(5, double)
│
└── operation = double
│
└── double(5) = 10
apply(5, square)
│
└── operation = square
│
└── square(5) = 25
The Three Pillars: map, filter, fold
These three higher-order list functions cover the vast majority of collection processing:
map — transform each element
import gleam/list
let prices = [100, 200, 300]
// Add 18% tax to every price
let with_tax = list.map(prices, fn(p) {
int.to_float(p) *. 1.18
})
// [118.0, 236.0, 354.0]
map Visual
──────────────────────────────────────────────────
Input: [100, 200, 300 ]
×1.18 ×1.18 ×1.18
Output: [118.0, 236.0, 354.0]
filter — keep matching elements
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evens = list.filter(numbers, fn(n) { n % 2 == 0 })
// [2, 4, 6, 8, 10]
fold — reduce to one value
let scores = [80, 95, 70, 88]
let total = list.fold(scores, 0, fn(acc, score) { acc + score })
// 333
let average = total / list.length(scores)
// 83
Chaining Higher-Order Functions
import gleam/list
let students = [
#("Arun", 45),
#("Bhavna", 78),
#("Chetan", 62),
#("Deepika", 91),
#("Esha", 38)
]
let top_names =
students
|> list.filter(fn(s) { s.1 >= 60 }) // keep passing students
|> list.map(fn(s) { s.0 }) // extract names only
|> list.sort(string.compare) // alphabetical order
// ["Bhavna", "Chetan", "Deepika"]
Pipeline Flow
──────────────────────────────────────────────────
All 5 students
↓ filter (score >= 60)
[Bhavna(78), Chetan(62), Deepika(91)]
↓ map (extract name)
["Bhavna", "Chetan", "Deepika"]
↓ sort
["Bhavna", "Chetan", "Deepika"]
Functions That Return Functions
A function can produce another function as its output:
pub fn make_adder(n: Int) -> fn(Int) -> Int {
fn(x) { x + n }
}
let add5 = make_adder(5)
let add10 = make_adder(10)
add5(3) // 8
add10(3) // 13
Function Factory Diagram
──────────────────────────────────────────────────
make_adder(5)
└── returns: fn(x) { x + 5 } ← stored as add5
make_adder(10)
└── returns: fn(x) { x + 10 } ← stored as add10
add5(3) → 3 + 5 = 8
add10(3) → 3 + 10 = 13
Partial Application
Gleam does not have built-in currying, but you achieve partial application by returning a function:
pub fn multiply(a: Int) -> fn(Int) -> Int {
fn(b) { a * b }
}
let double = multiply(2)
let triple = multiply(3)
list.map([1, 2, 3, 4], double) // [2, 4, 6, 8]
list.map([1, 2, 3, 4], triple) // [3, 6, 9, 12]
Function Type Syntax
Function Type Signatures
──────────────────────────────────────────────────
fn(Int) -> Int
→ Takes one Int, returns one Int
fn(String, Int) -> Bool
→ Takes String and Int, returns Bool
fn(fn(Int) -> Int, Int) -> Int
→ Takes a function and an Int, returns Int
fn(Int) -> fn(Int) -> Int
→ Takes an Int, returns a function
Practical Example — Report Generator
import gleam/list
import gleam/io
type Sale {
Sale(product: String, amount: Float, region: String)
}
pub fn total_by(sales: List(Sale), key: fn(Sale) -> Float) -> Float {
list.fold(sales, 0.0, fn(acc, s) { acc +. key(s) })
}
pub fn filter_by_region(sales: List(Sale), region: String) -> List(Sale) {
list.filter(sales, fn(s) { s.region == region })
}
pub fn main() {
let data = [
Sale("Laptop", 50000.0, "North"),
Sale("Phone", 25000.0, "South"),
Sale("Tablet", 30000.0, "North"),
Sale("Watch", 15000.0, "South")
]
let north_total =
data
|> filter_by_region("North")
|> total_by(fn(s) { s.amount })
io.debug(north_total) // 80000.0
}
Key Points
Higher-Order Functions Summary
──────────────────────────────────────────────────
1. Functions are values — pass and return them freely
2. map(list, f) → transform each element
3. filter(list, pred) → keep elements where pred = True
4. fold(list, init, f)→ reduce list to one value
5. Returning functions enables partial application
6. Chain with |> for readable pipelines
7. Function type: fn(ArgType) -> ReturnType
Higher-order functions eliminate loops and replace them with intention-revealing operations. filter, map, and fold describe what you want — not how to achieve it step by step. The result is shorter, more readable, and easier to test code.
