Credence.Pattern.NoEagerWithIndexInReduce
(credence v0.4.2)
Copy Markdown
Performance rule: Detects Enum.with_index/1 passed directly as the
enumerable argument to Enum.reduce/3 (or piped into it).
Enum.with_index/1 is eager — it traverses the entire list and allocates
a new list of {value, index} tuples before Enum.reduce begins. This
doubles memory consumption for large lists.
Bad
Enum.reduce(Enum.with_index(list), acc, fn {val, idx}, acc -> ... end)
list |> Enum.with_index() |> Enum.reduce(acc, fn ...)Good
# Option 1 (:stream strategy): Use Stream.with_index for lazy evaluation
list |> Stream.with_index() |> Enum.reduce(acc, fn {val, idx}, acc -> ... end)
# Option 2 (:reduce strategy): Track the index in the accumulator
list
|> Enum.reduce({0, acc}, fn val, {idx, acc} -> {idx + 1, ...} end)
|> elem(1)Fix strategy
Controlled by the @fix_strategy module attribute (default: :stream).
Can also be overridden per-call via opts[:fix_strategy].
# Use stream (default):
NoEagerWithIndexInReduce.fix(source, [])
# Use reduce:
NoEagerWithIndexInReduce.fix(source, fix_strategy: :reduce)The :reduce strategy falls back to :stream when the anonymous
function shape doesn't match the expected fn {val, idx}, acc -> body end
pattern (e.g. multi-clause fns or complex destructuring).