Reading — step 1 of 7
Learn
Elixir's for is a comprehension — like Python's list comprehension or Haskell's, but with Elixir's pattern matching. Programming Elixir treats it as a powerful generator/filter/transformer.
Basic syntax
for n <- [1, 2, 3, 4, 5], do: n * 2
# [2, 4, 6, 8, 10]
Reads: "for each n in [1..5], yield n * 2."
With a filter (guard)
for n <- 1..10, rem(n, 2) == 0, do: n * n
# [4, 16, 36, 64, 100]
The second clause is a filter — only items where the expression is truthy continue.
Multiple generators
for x <- [1, 2, 3], y <- [:a, :b], do: {x, y}
# [{1, :a}, {1, :b}, {2, :a}, {2, :b}, {3, :a}, {3, :b}]
Multiple generators give a CARTESIAN PRODUCT — like nested loops.
With a filter constraining the cross:
for x <- 1..3, y <- 1..3, x != y, do: {x, y}
# [{1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}]
Pattern matching in generators
users = [{1, "Ada"}, {2, "Bob"}, {3, "Carol"}]
for {id, name} <- users, do: "#{id}: #{name}"
# ["1: Ada", "2: Bob", "3: Carol"]
Destructure each element. If the pattern doesn't match, the item is SKIPPED (not an error). Useful for filtering by shape:
mixed = [{:ok, 1}, {:error, "bad"}, {:ok, 2}, {:ok, 3}]
for {:ok, value} <- mixed, do: value * 10
# [10, 20, 30] — :error tuples skipped
The pattern {:ok, value} matches only success tuples. Errors silently dropped.
:into — produce other collections
By default for produces a list. Use :into for maps, MapSets, etc:
for {k, v} <- [{:a, 1}, {:b, 2}], into: %{}, do: {k, v * 10}
# %{a: 10, b: 20}
for n <- [1, 1, 2, 3, 3], into: MapSet.new(), do: n
# MapSet.new([1, 2, 3])
The :into accumulator collects results. Any Collectable works.
:reduce — accumulator-based comprehensions
for n <- 1..5, reduce: 0 do
acc -> acc + n
end
# 15 — sum
Like Enum.reduce in comprehension form. Pattern-match the accumulator across iterations:
for n <- 1..10, reduce: %{evens: [], odds: []} do
%{evens: e, odds: o} when rem(n, 2) == 0 ->
%{evens: [n | e], odds: o}
%{evens: e, odds: o} ->
%{evens: e, odds: [n | o]}
end
Reduce with branching — useful for grouping logic.
Bitstring comprehensions
Process binary data:
pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
# [{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
Destructure 3 bytes at a time. Useful for binary protocols, image data.
When use comprehension vs Enum / pipeline?
- Comprehension — multiple generators / Cartesian products,
:intofor non-list output, complex:reducepatterns - Pipeline (
|>) — transformations on a single collection, deeply composable Enumfunctions — when the right named function exists
Pipelines tend to read better for long sequences of single transformations. Comprehensions shine when you need multiple sources or shape-filtering.
Common mistakes
- Forgetting
do:on one-liners —for n <- list, n * 2is a syntax error.for n <- list, do: n * 2works. - Multi-line without
do/end— for multi-clause:reduce, use the block form:for ... reduce: 0 do ... end. - Expecting filtering to error on bad shapes — pattern non-matches SKIP, not error. Surprising if you didn't intend to filter.
- Comprehension when pipeline is clearer —
for n <- list, do: f(n)is justEnum.map(list, &f/1). Use the simpler form. :intowith mutable expectation — Elixir is immutable.:intoproduces a new collection; original is unchanged.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…