← Code Compare

Recursion

The BEAM has no mutable loops, so recursion is the control structure for iteration. The idiom to learn is tail recursion with an accumulator: each call passes the running total forward so the compiler can reuse the stack frame, turning recursion into a constant-space loop. Watch how every language splits the work into a base case (empty list) and a recursive case (head plus the recursion on the tail), whether via multiple function clauses, case, or match.

Show: ErlangElixirGleamLFELuerl
Erlang
-module(recur).
-export([sum/1]).

sum(List) -> sum(List, 0).

%% base case: empty list returns the accumulator
sum([], Acc) -> Acc;
%% recursive case: add the head, recurse on the tail
sum([H | T], Acc) -> sum(T, Acc + H).

%% sum([1,2,3,4,5]) =:= 15
%% (lists:sum/1 does the same thing in the standard library)

Erlang leans on multiple function clauses with head/tail pattern matching; the two-argument helper carries the accumulator, and the [H | T] clause makes the tail call in last position.

Elixir
defmodule Recur do
  # public entry point seeds the accumulator
  def sum(list), do: sum(list, 0)

  # base case: empty list returns the accumulator
  defp sum([], acc), do: acc
  # recursive case: add the head, recurse on the tail
  defp sum([head | tail], acc), do: sum(tail, acc + head)
end

# Recur.sum([1, 2, 3, 4, 5]) == 15
# Idiomatic Elixir would just write: Enum.sum([1, 2, 3, 4, 5])
# or reduce it: Enum.reduce([1, 2, 3, 4, 5], 0, &+/2)

Elixir mirrors Erlang's clause-based approach with a private helper, but everyday code reaches for Enum.reduce/3 or Enum.sum/1 instead of hand-rolling the recursion.

Gleam
import gleam/int
import gleam/io

// public entry point seeds the accumulator
pub fn sum(list: List(Int)) -> Int {
  do_sum(list, from: 0)
}

// labelled accumulator argument; `case` splits the two shapes
fn do_sum(list: List(Int), from acc: Int) -> Int {
  case list {
    [] -> acc
    [head, ..tail] -> do_sum(tail, from: acc + head)
  }
}

pub fn main() {
  io.println(int.to_string(sum([1, 2, 3, 4, 5])))
  // prints "15"; gleam/list also offers list.fold([1,2,3,4,5], 0, int.add)
}

Gleam is statically typed, so the List(Int) -> Int signature is checked at compile time; case exhaustively matches the empty and [head, ..tail] shapes, and the labelled from: argument keeps the accumulator call readable.

LFE
(defmodule recur
  (export (sum 1)))

;; public entry point seeds the accumulator
(defun sum (list)
  (sum list 0))

;; two clauses: base case, then the recursive case
(defun sum
  (('() acc) acc)
  (((cons head tail) acc) (sum tail (+ acc head))))

;; (recur:sum '(1 2 3 4 5)) => 15
;; (lists:sum '(1 2 3 4 5)) works too via Erlang interop

LFE writes Erlang's multi-clause recursion as S-expressions: the second defun lists one (pattern body) clause per line, using '() for the empty list and (cons head tail) to destructure the head and tail.

Luerl
-- Lua's only compound type is the table, used here as a 1-indexed array.
-- `i` plays the role of "the rest of the list" instead of head/tail.
local function sum(list, i, acc)
  i = i or 1
  acc = acc or 0
  -- base case: walked past the last element
  if list[i] == nil then
    return acc
  end
  -- recursive case: add this element, advance the index (tail call)
  return sum(list, i + 1, acc + list[i])
end

print(sum({1, 2, 3, 4, 5}))  --> 15
-- Lua guarantees proper tail calls, so this runs in constant stack space.

Luerl runs Lua, which lacks head/tail pattern matching, so recursion advances an integer index over a table; Lua's guaranteed proper tail calls make return sum(...) loop in constant stack space.