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)