Search code examples
pattern-matchingsmlmlperfect-square

Redundant Pattern Matching


I am trying to write a function that finds whether or not a given number n is a perfect square. Here's my attempt:

local
  fun perfect_square_iter x z = let val sqr = z * z in
    case (x,z) of
        (sqr,_) => true
      | (_, 0) => false
      | _ => perfect_square_iter x (z - 1)
    end
in fun perfect_square n = perfect_square_iter n n
end

Now, when I try to run this with sml myfile.sml, I get the following error:

lab03.sml:17.5-20.43 Error: match redundant
          (sqr,_) => ...
    -->   (_,0) => ...
    -->   _ => ...

/usr/lib/smlnj/bin/sml: Fatal error -- Uncaught exception Error with 0
 raised at ../compiler/FLINT/trans/translate.sml:1735.13-1735.21

This doesn't seem to be a redundant pattern to me, because it only matches two constants and then anything else. Why does the compiler see this as redundant?


Solution

  • sqr is not a constant, even given your let-binding of it. Syntactically, it is a variable and in the language of patterns all variables are free variables which match anything. Thus your pattern (sqr,_) matches all arguments. The value before the comma will be bound to sqr in the body of that clause (the RHS of the =>), thus hiding your binding of it to z*z, and the value after it will be discarded. This covers all possible cases, hence the rest of your matches are redundant.

    You can verify that variable matching in a pattern introduces a new local scope by considering the following (absolutely awful) code:

    fun f xs =
        let val x = 5 in
            case xs of
                [] => 0
            |   x::xs => x
        end;
    

    It compiles, but then, e.g f [1,7,10] returns 1 instead of 5.

    For your code, you need to use if ... then ... else rather than pattern matching to handle the case of x = sqr. Something like

    (_,0) => false
    | (_,_) => if x = sqr ...
    

    (This is assuming that you still want to use pattern matching, but since you can't use it the way that you wanted to a more radical restructuring of the code that e.g. dispenses with the let might be appropriate).