Credence.Rule.NoEnumAtInLoop
(credence v0.3.0)
Copy Markdown
Performance rule: Detects Enum.at/2 inside looping constructs
(Enum.reduce, Enum.map, Enum.each, Enum.filter, Enum.flat_map,
for comprehensions) or recursive functions.
Enum.at/2 is O(n) on linked lists because it traverses from the head to
the given index. Inside a loop this compounds to O(n²) or worse. This is
one of the most common performance traps for developers coming from
languages with array-based lists.
This rule is not auto-fixable because every remedy requires non-local
changes: inserting List.to_tuple/1 in an outer scope, restructuring a
callback signature for Enum.with_index/1, or rewriting the algorithm to
use pattern matching. These transformations depend on surrounding context
and cannot be expressed as a mechanical AST node swap.
Bad
defp expand(graphemes, left, right, count) do
if Enum.at(graphemes, left) == Enum.at(graphemes, right) do
expand(graphemes, left - 1, right + 1, count)
else
0
end
end
Enum.reduce(0..n, 0, fn i, acc ->
acc + Enum.at(list, i)
end)Good
# Option 1: Convert to tuple for O(1) indexed access
tuple = List.to_tuple(graphemes)
elem(tuple, left) == elem(tuple, right)
# Option 2: Use pattern matching / recursion on the list directly
defp process([head | tail]), do: ...
# Option 3: Use Enum.with_index or Enum.zip to pair values with indices
Enum.reduce(Enum.with_index(list), 0, fn {val, _idx}, acc -> acc + val end)