Functions
Functions are the heart of every BEAM language, and most of them lean on pattern matching across multiple clauses rather than if/else ladders. Notice how Erlang, Elixir, and LFE dispatch on guards (when) while statically typed Gleam exhaustively matches with case, and Lua (Luerl) falls back to ordinary conditionals since it has no clauses. Each example also shows a higher-order use - passing the function to a list operation like map - which is the everyday way you reuse logic on the BEAM.
-module(classify).
-export([classify/1, demo/0]).
%% Multiple clauses with guards; tested top-to-bottom.
classify(N) when N < 0 -> negative;
classify(0) -> zero;
classify(_N) -> positive.
demo() ->
%% Higher-order: hand classify/1 to lists:map as a fun.
lists:map(fun classify/1, [-3, 0, 7]).
%% => [negative, zero, positive]
Erlang defines one function as several clauses separated by ;, each with its own head and optional when guard; fun classify/1 turns it into a value you can pass to lists:map/2.
defmodule Classify do
# Each `def` clause matches a pattern or guard, in order.
def classify(n) when n < 0, do: :negative
def classify(0), do: :zero
def classify(_n), do: :positive
end
# Higher-order: capture the function with &/1 and pipe through Enum.map.
[-3, 0, 7]
|> Enum.map(&Classify.classify/1)
# => [:negative, :zero, :positive]
Elixir spreads a function over multiple def clauses with when guards, then uses the &Module.fun/1 capture and the |> pipe to map it over a list.
import gleam/list
import gleam/order
import gleam/int
pub type Sign {
Negative
Zero
Positive
}
// One clause; an exhaustive `case` over the comparison result.
pub fn classify(n: Int) -> Sign {
case int.compare(n, 0) {
order.Lt -> Negative
order.Eq -> Zero
order.Gt -> Positive
}
}
pub fn main() {
// Higher-order: pass classify as a first-class function to list.map.
[-3, 0, 7]
|> list.map(classify)
|> echo
// => [Negative, Zero, Positive]
}
Gleam has a single pub fn whose case must be exhaustive, returning a sound custom type instead of an atom; classify is passed by name to list.map with no null or exceptions in sight.
(defmodule classify
(export (classify 1) (demo 0)))
;; Multiple clauses with guards inside one defun.
(defun classify
((n) (when (< n 0)) 'negative)
((0) 'zero)
((_n) 'positive))
(defun demo ()
;; Higher-order: #'classify/1 grabs the function as a value.
(lists:map #'classify/1 (list -3 0 7)))
;; => (negative zero positive)
LFE writes the clauses as parenthesised pattern lists with (when ...) guards inside a single defun, and #'classify/1 is the Lisp reader macro for a function reference passed to lists:map.
-- Lua has no clauses or guards, so branch with if/elseif.
local function classify(n)
if n < 0 then
return "negative"
elseif n == 0 then
return "zero"
else
return "positive"
end
end
-- Higher-order: functions are values; map one over a table.
local function map(fn, t)
local out = {}
for i, v in ipairs(t) do out[i] = fn(v) end
return out
end
for _, s in ipairs(map(classify, { -3, 0, 7 })) do
print(s)
end
-- => negative zero positive
Lua expresses the same logic with a plain if/elseif/else since it lacks pattern matching, but functions are still first-class values, so a hand-rolled map can take classify as an argument.