Gleam Functions

Functions are the building blocks of every Gleam program. A function groups related code under a name, accepts inputs, performs work, and returns a result. Well-written functions make programs readable, testable, and reusable.

Function Anatomy


Parts of a Gleam Function
──────────────────────────────────────────────────────────
pub fn add(a: Int, b: Int) -> Int {
 │    │    └────────────┘    └─┘
 │    │      parameters      return type
 │    └── function name
 └── visibility (pub = public)

  a + b      ← function body (expression returned)
}

Every function consists of these parts: visibility keyword, fn keyword, name, parameters with types, return type, and body.

Writing a Simple Function

pub fn square(n: Int) -> Int {
  n * n
}

The function body is the last expression — its value becomes the return value. Gleam does not use a return keyword. The result of the last expression is automatically returned.


Function Flow Diagram
──────────────────────────────────────────────────
square(5)
  │
  └── body: n * n
             ↓
           5 * 5
             ↓
            25    ← returned to caller

Calling Functions

import gleam/io

pub fn square(n: Int) -> Int {
  n * n
}

pub fn main() {
  let result = square(7)
  io.debug(result)   // 49
}

Call a function by writing its name followed by arguments in parentheses. The arguments must match the parameter types — Gleam verifies this at compile time.

Functions with Multiple Parameters

pub fn rectangle_area(width: Float, height: Float) -> Float {
  width *. height
}

pub fn greet(name: String, language: String) -> String {
  case language {
    "Hindi"  -> "नमस्ते, " <> name
    "French" -> "Bonjour, " <> name
    _        -> "Hello, " <> name
  }
}

Labeled Arguments

Gleam supports labeled arguments. Labels make function calls self-documenting — you see exactly what each argument means at the call site.

pub fn send_email(to recipient: String, subject text: String) -> Nil {
  // label: to   parameter name: recipient
  // label: subject  parameter name: text
}

// Calling with labels:
send_email(to: "user@mail.com", subject: "Welcome")

Labeled vs Unlabeled
──────────────────────────────────────────────────
Without labels:
  send_email("user@mail.com", "Welcome")
  ↑ Which is which? Not obvious.

With labels:
  send_email(to: "user@mail.com", subject: "Welcome")
  ↑ Crystal clear.

Inside the function body, use the parameter name (not the label). Outside the function, callers use the label.

Ignoring Labels at the Call Site

If a label starts with an underscore, it is optional:

pub fn log(_level level: String, message: String) -> Nil {
  // ...
}

// Call without label:
log("info", message: "Server started")

Return Types

Every function declares what it returns after the -> arrow. This contract between the function and its callers is checked by the compiler.


Return Type Examples
──────────────────────────────────────────────────
pub fn get_name() -> String { "Gleam" }
pub fn get_count() -> Int { 42 }
pub fn is_valid() -> Bool { True }
pub fn process() -> Nil { io.println("done") }
pub fn get_items() -> List(String) { ["a", "b"] }

A function that produces no meaningful result returns Nil. Functions that could fail return Result or Option — covered in later topics.

Functions Are Values

In Gleam, functions are first-class values. You store them in variables, pass them to other functions, and return them from functions.

let double = fn(n: Int) -> Int { n * 2 }
let result = double(5)   // 10

This anonymous function (sometimes called a lambda) has no name. You assign it to the variable double and call it through that variable.

Passing Functions as Arguments

pub fn apply(value: Int, operation: fn(Int) -> Int) -> Int {
  operation(value)
}

pub fn triple(n: Int) -> Int {
  n * 3
}

pub fn main() {
  let result = apply(4, triple)
  io.debug(result)   // 12
}

Higher-Order Function Diagram
──────────────────────────────────────────────────
apply(4, triple)
         │
         └── passes triple as the "operation"
                   │
                   └── triple(4) = 12
                   └── returned from apply

Private vs Public Functions


Visibility Rules
──────────────────────────────────────────────────
pub fn greet() -> String { ... }
  → Visible from other modules
  → Part of the module's public interface

fn helper() -> String { ... }
  → Visible only inside this file
  → Internal implementation detail

Use pub for functions that other modules need. Keep everything else private. This principle — exposing only what is necessary — is called encapsulation.

The Last Expression is the Return Value

Gleam returns the value of the last expression automatically. You cannot use return anywhere in the function body.

pub fn classify_score(score: Int) -> String {
  let label = case score {
    s if s >= 90 -> "Excellent"
    s if s >= 70 -> "Good"
    s if s >= 50 -> "Average"
    _ -> "Below Average"
  }
  label   // ← this is the return value
}

Functions Calling Other Functions

pub fn celsius_to_fahrenheit(c: Float) -> Float {
  c *. 9.0 /. 5.0 +. 32.0
}

pub fn body_temp_status(temp_c: Float) -> String {
  let temp_f = celsius_to_fahrenheit(temp_c)
  case temp_f >= 100.4 {
    True  -> "Fever detected"
    False -> "Normal temperature"
  }
}

Call Chain Diagram
──────────────────────────────────────────────────
body_temp_status(38.5)
    │
    ├── celsius_to_fahrenheit(38.5)
    │       │
    │       └── 38.5 × 9 / 5 + 32 = 101.3
    │
    └── 101.3 >= 100.4 → True → "Fever detected"

Recursive Functions

A function can call itself. This is called recursion. Gleam uses recursion where other languages use loops.

pub fn factorial(n: Int) -> Int {
  case n {
    0 -> 1
    _ -> n * factorial(n - 1)
  }
}
// factorial(5) = 5 × 4 × 3 × 2 × 1 = 120

Practical Example — Bill Calculator

import gleam/io
import gleam/float

pub fn calculate_tip(bill: Float, percentage: Float) -> Float {
  bill *. percentage /. 100.0
}

pub fn split_bill(total: Float, people: Int) -> Float {
  total /. int.to_float(people)
}

pub fn final_bill(bill: Float, tip_percent: Float, people: Int) -> Float {
  let tip = calculate_tip(bill, tip_percent)
  let total = bill +. tip
  split_bill(total, people)
}

pub fn main() {
  let per_person = final_bill(1000.0, 10.0, 4)
  io.debug(per_person)   // 275.0
}

Bill Calculation Flow
──────────────────────────────────────────────────
bill = 1000.0, tip = 10%, people = 4

calculate_tip(1000.0, 10.0) = 100.0
total = 1000.0 + 100.0 = 1100.0
split_bill(1100.0, 4) = 275.0 per person

Key Rules for Gleam Functions


Summary
──────────────────────────────────────────────────
1. Define with fn keyword
2. All parameters must have type annotations
3. Return type follows the -> arrow
4. Last expression is the return value — no return keyword
5. pub makes a function visible outside the module
6. Functions are values — store and pass them freely
7. Labeled arguments make call sites readable

Functions in Gleam are pure by default — a function with the same inputs always produces the same output, with no hidden side effects. This predictability makes testing straightforward and debugging fast.

Leave a Comment

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