luksan
luksan

Reputation: 7757

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.

Upvotes: 6

Views: 1043

Answers (2)

Tomas Petricek
Tomas Petricek

Reputation: 243051

I guess you found another case (aside from when clauses and partial active patterns) where the compiler's decision procedure is not powerful enough. (That's why the warning message says may indicate :-)).

If you wanted to get the same functionality without warnings, then you could use a complete active pattern like this (but in reality, for lists, I would probably just go with _ for the empty list case as @pad suggests):

let (|Empty|NonEmpty|) l =
  match l with [] -> Empty | x::xs -> NonEmpty(x, xs, l)

let foo = function  
  | Empty -> 0
  | NonEmpty(x, xs, l) -> 1

Upvotes: 4

pad
pad

Reputation: 41290

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".

Upvotes: 6

Related Questions