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 when guard verbatim — conditions are never folded into the head pattern. Folding count == 0 into the literal pattern {candidate, 0} would diverge: count == 0 is true for both 0 and 0.0, but the pattern 0 matches 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 (so if's truthiness matches a guard's strict true) 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 else branch, so the final clause is a genuine catch-all that maps to that branch. An innermost if without else is left alone — turning it into a catch-all would answer where the original returned nil.