Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# pattern matching oddity

Consider the following nonsense lambda:

function
| [] -> "Empty list"
| hd::tl -> "Not so empty list"

This works fine. Now I rewrite it as follows:

function
| [] -> "Empty list"
| hd::tl & l -> "Not so empty list"

Again, for nonsense reasons (and I know that I can achieve the same effect by using as instead of &, but this all has to do with a code-golf problem that's not pertinent to this question). Now the F# compiler tells me:

warning FS0025: Incomplete pattern matches on this expression. For example, the value '[]' may indicate a case not covered by the pattern(s).

This doesn't make any sense - I am explicitly handling the case of [] in the first rule. I don't see what changed from the first function to the second with respect to []; neither function's second rule would have matched it yet only the second function gives the warning. All I did was add an additional pattern that matches anything.

Of course, invoking the second function with an empty list does succeed.

Is there a valid reason why this warning occurred, or does the F# pattern validation simply have some quirks? I could see having some cases like this when more advanced patterns are employed, but this seems like a pretty basic one. Even if the problem can't be solved generally, it seems like this type of case would be common enough to merit special handling in the compiler.

like image 514
luksan Avatar asked Aug 23 '13 05:08

luksan


1 Answers

I think F# compiler is being practical in this case.

In the end, the second rule can be expressed as a constraint on input list xs:

xs = hd :: tl && xs = l

F# compiler doesn't seem to explore && constraints. This is reasonable because constraints can be arbitrarily complex and the use of & is quite rare.

We have a similar problem with partial active patterns:

let (|Empty|_|) = function
    | [] -> Some()
    | _ -> None

let (|NonEmpty|_|) = function
    | _ :: _ -> Some()
    | _ -> None

// warning FS0025
let f = function
    | Empty -> "Empty list"
    | NonEmpty -> "Not so empty list"

To fix this issue, you can:

  • Use as instead of &, which is more appropriate since l is just a binding.
  • Add a wildcard pattern to the end to eliminate the warning. I usually write

    let f =
        function
        | hd::tl & l -> "Not so empty list"
        | _ -> "Empty list"
    
  • Suppress the warning using nowarn "25".

like image 164
pad Avatar answered Oct 06 '22 18:10

pad