Credence.Rule.NoEagerWithIndexInReduce (credence v0.3.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).