Credence.Pattern.PreferMultiClauseReduceFn
(credence v0.8.0)
Copy Markdown
Detects a two-argument anonymous function whose single clause is a nested
if/else chain and rewrites it as a multi-clause pattern-matching fn,
turning each if condition into a when guard.
Bad
Enum.reduce(list, {nil, 0}, fn element, {candidate, count} ->
if count == 0 do
{element, 1}
else
if element == candidate do
{candidate, count + 1}
else
{candidate, count - 1}
end
end
end)Good
Enum.reduce(list, {nil, 0}, fn
element, {candidate, count} when count == 0 -> {element, 1}
element, {candidate, count} when element == candidate -> {candidate, count + 1}
_element, {candidate, count} -> {candidate, count - 1}
end)Safety
The rewrite is deliberately conservative so it gives the exact same answer on every input:
- Each condition becomes a
whenguard verbatim — conditions are never folded into the head pattern. Foldingcount == 0into the literal pattern{candidate, 0}would diverge:count == 0is true for both0and0.0, but the pattern0matches only the integer. - The rule fires only when every condition is a comparison
(
==,!=,===,!==,<,>,<=,>=) whose operands are plain variables or literals. Such conditions always return a boolean (soif's truthiness matches a guard's stricttrue) and never raise (so the guard's error-swallowing can't change the result). Conditions with computed leaves (e.g.count + 1 == 0) or non-comparison calls are left alone. - The chain must end with an
elsebranch, so the final clause is a genuine catch-all that maps to that branch. An innermostifwithoutelseis left alone — turning it into a catch-all would answer where the original returnednil.