Credence.Pattern.NoListFoldl (credence v0.8.0)

Copy Markdown

Detects List.foldl/3 on a provably-list argument and suggests Enum.reduce/3.

List.foldl(list, acc, fun) and Enum.reduce(list, acc, fun) fold a list identically — same left-to-right order, same fun.(elem, acc) argument order. Enum.reduce/3 is the idiomatic Elixir fold; List.foldl/3 usually signals code ported from Erlang.

Safe core — provably-list argument only

The two are not interchangeable on a non-list enumerable: List.foldl raises FunctionClauseError on a map/range/stream, whereas Enum.reduce succeeds — a value-vs-crash divergence. So the rule fires only when the folded argument is provably a list: a list literal, a ++, or a (possibly piped) call to a list-returning function (Enum.map/filter/…, Map.keys, String.split, List.*, …). A bare variable is left alone — we can't prove it is a list, and an expression rewrite can't add an is_list guard.

List.foldr/3 is intentionally not covered: it is a right fold, needing Enum.reduce(Enum.reverse(list), acc, fun) — more verbose than List.foldr.

Bad

List.foldl([1, 2, 3], 0, fun)

nums |> Enum.filter(&pos?/1) |> List.foldl(0, fun)

Good

Enum.reduce([1, 2, 3], 0, fun)

nums |> Enum.filter(&pos?/1) |> Enum.reduce(0, fun)