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.

Leave a Comment

Your email address will not be published. Required fields are marked *