← Code Compare

Ranges & Iteration

One number-crunching task in all five BEAM languages: sum the integers 1..10 (the answer is 55). Watch how each language produces the sequence and then folds it - Elixir has a first-class 1..10 range plus list comprehensions, Erlang materialises the list with lists:seq/2, Gleam folds the ints with the typed built-in int.range, LFE calls the same lists:seq in S-expressions, and Luerl drops to Lua's classic three-part numeric for loop with a mutable accumulator.

Show: ErlangElixirGleamLFELuerl
Erlang
-module(ranges).
-export([run/0]).

run() ->
    %% lists:seq/2 materialises the range as a list: [1,2,...,10]
    %% then lists:sum/1 folds it down to a single integer.
    lists:sum(lists:seq(1, 10)).
%% run() =:= 55
%%
%% A list comprehension can filter/transform on the way:
%% lists:sum([X || X <- lists:seq(1, 10)]) also yields 55.

Erlang has no range type: lists:seq(1, 10) eagerly builds the list [1..10], and lists:sum/1 is the ready-made fold for addition. List comprehensions ([X || X <- ...]) sit on top of the same generated list when you need to transform or filter.

Elixir
# 1..10 is a real Range value (lazy, O(1) to create)
sum = 1..10 |> Enum.sum()
# => 55

# A comprehension fits when you transform/filter first;
# Enum.sum folds the resulting list:
squares_sum = for n <- 1..10, do: n * n
# squares_sum is [1, 4, 9, ...]; Enum.sum(squares_sum) == 385

# Or fold explicitly with the captured + operator:
folded = Enum.reduce(1..10, 0, &+/2)
# folded == 55

Elixir's 1..10 is a genuine Range struct that you pipe straight into Enum.sum/1; comprehensions (for n <- 1..10) and Enum.reduce(1..10, 0, &+/2) are the idiomatic ways to map/filter or fold over the same range without ever building an intermediate list by hand.

Gleam
import gleam/int
import gleam/io

pub fn main() {
  // int.range folds straight over the ints: `from` is inclusive and `to`
  // is exclusive, so 1..11 visits 1, 2, ..., 10.
  let total =
    int.range(from: 1, to: 11, with: 0, run: fn(acc, n) { acc + n })
  // total == 55

  // int.sum is the typed convenience fold for addition over a List(Int):
  let also_total = int.sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  // also_total == 55

  io.println(int.to_string(total))
  io.println(int.to_string(also_total))
  // both print "55"
}

Gleam's int.range is a built-in fold over the ints from from (inclusive) to to (exclusive), so to: 11 reaches 10; you pass the seed with with: and the reducer fn(acc, n) with run:. int.sum is the typed convenience fold for addition over a List(Int), and int.to_string is required because the compiler never coerces an Int to a String. (list.range, which used to build a List(Int), was deprecated in stdlib v0.69 and removed in v0.71.)

LFE
(defmodule ranges
  (export (run 0)))

(defun run ()
  ;; LFE is Erlang in S-expressions, so it calls the same library:
  ;; (lists:seq 1 10) builds (1 2 ... 10), (lists:sum ...) folds it.
  (lists:sum (lists:seq 1 10)))
;; (ranges:run) => 55
;;
;; LFE comprehensions exist too:
;; (lc ((<- x (lists:seq 1 10))) x) rebuilds the list before summing.

LFE reaches for the very same lists:seq/lists:sum as Erlang, just written with prefix S-expressions; the lc list-comprehension macro ((lc ((<- x ...)) body)) is available when you need to transform or filter the generated sequence.

Luerl
-- Lua has no range type; the idiom is the numeric for loop:
-- `for i = start, stop[, step]` counts inclusively from start to stop.
local sum = 0
for i = 1, 10 do        -- i takes 1, 2, ..., 10
  sum = sum + i         -- mutable accumulator
end

print(sum)  --> 55

-- A step of 2 would visit 1, 3, 5, 7, 9:
-- for i = 1, 10, 2 do ... end

Standard Lua (what Luerl runs) iterates with the three-part numeric for i = start, stop, step loop, whose bounds are inclusive; you accumulate into a mutable local rather than folding, which is the natural Lua style since it ships no higher-order range library.