Credence.Pattern.NoReduceForMapBuilding
(credence v0.7.1)
Copy Markdown
Detects Enum.reduce/3 with an empty-map accumulator %{} and a body
consisting solely of Map.put(acc, key, value), and suggests Map.new/2
instead. Also detects Enum.reduce/3 building a MapSet via
MapSet.put/2 and suggests MapSet.new/1.
Why this matters
LLMs frequently produce:
Enum.reduce(list, %{}, fn x, acc ->
Map.put(acc, x, String.length(x))
end)when the idiomatic Elixir equivalent is:
Map.new(list, fn x -> {x, String.length(x)} end)Similarly, LLMs may build a MapSet via reduce:
Enum.reduce(list, MapSet.new(), fn x, acc -> MapSet.put(acc, x) end)when the idiomatic equivalent is:
MapSet.new(list)Map.new/2 and MapSet.new/1 communicate intent directly and avoid
manual accumulator threading.
Flagged patterns
| Pattern | Suggested replacement |
|---|---|
Enum.reduce(enum, %{}, fn x, acc -> Map.put(acc, k, v) end) | Map.new(enum, fn x -> {k, v} end) |
| enum |> Enum.reduce(%{}, fn x, acc -> Map.put(acc, k, v) end) | enum |> Map.new(fn x -> {k, v} end) |
| Enum.reduce(enum, MapSet.new(), fn x, acc -> MapSet.put(acc, x) end) | MapSet.new(enum) |
| enum |> Enum.reduce(MapSet.new(), fn x, acc -> MapSet.put(acc, x) end) | enum |> MapSet.new() |
Only the simplest case is matched: the anonymous function body must be
a single Map.put(acc, key, value) or MapSet.put(acc, value) call
(or a single-expression block), and neither key/value nor the
MapSet value may reference acc.