Redundant cases and redundant case analysis

Code has redundant case analysis if it has either of these two faults:

  1. A clause that is (or can be) completely subsumed by another clause or clauses

  2. A case analysis that need not be performed

Fault #1 will always be accompanied by overlapping patterns. Fault #2 is independent of overlapping patterns.

Here's an example of Fault #1:

fun shiftLeft 0 n = n              (* FAULTY *)
  | shiftLeft 1 n = 2 * n
  | shiftLeft k n = 2 * shiftLeft (k - 1) n

The case for k = 1 can be handled just as well by the general case:

fun shiftLeft 0 n = n              (* CORRECTED *)
  | shiftLeft k n = 2 * shiftLeft (k - 1) n

Here's an example of Fault #2:

fun dropFirstInTwo ([],    [])    = raise Empty   (* FAULTY *)
  | dropFirstInTwo ([],    y::ys) = ([], ys)
  | dropFirstInTwo (x::xs, [])    = (xs, [])
  | dropFirstInTwo (x::xs, y::ys) = (xs, y::ys)

In the last two clauses, the case analysis on the second half of the pair is not needed:

fun dropFirstInTwo ([],    [])    = raise Empty   (* CORRECTED *)
  | dropFirstInTwo ([],    y::ys) = ([], ys)
  | dropFirstInTwo (x::xs, ys)    = (xs, ys)

In the corrected version, the compiler is doing strictly less work: if the first list is nonempty, the second list doesn't need to be scrutinized.