I'm reviving this thread because I've just run into this problem again. To make it clear what the problem is, here are some examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type Data = { b : bool }
type UU = UA of Data | UB of Data

// Incomplete pattern matches on this expression
let f = function
    | UA d when d.b -> ()
    | UA d when not d.b -> ()
    | UB _ -> ()

type UV = VA of bool | VB of bool

// No warning
let f' = function
    | VA true -> ()
    | VA false -> ()
    | VB _ -> ()

f and f' are very similar, and it's annoying to get the warning for f.
This problem arises in practice because discriminated unions of the form of UV are not very practical to use when it's not clear what kind of tuples you will need. Whenever you add, remove or reorder the datatype associated to a discriminator (is that what they are called?), you need to fix all references in your code.
One way to work around that problem is to declare unions using UU's form. However, you lose the ability to match on the data.

My suggestions:

1. Provide a way to name fields when declaring unions:

1
type UU = UA of b:bool | UB of b:bool

Alternatively:

2. Support simple when clauses when checking pattern coverage

In any case, I think the text of the warning is misleading. As far as I can see, f covers all cases. The warning should say "Potentially incomplete pattern matches on this expression".

By on 12/22/2010 2:07 AM ()

In your particular case you can rewrite your match cases as

1
2
3
4
5
let f = function
    | UA {b = true}  -> ()
    | UA {b = false} -> ()
    | UB _ -> ()
By on 12/22/2010 2:13 AM ()

desco: Thanks a lot, I did not know this was possible! This is really all I need in my situation.

By on 12/22/2010 7:08 AM ()

Guards are never considered complete, so if you don't have the cases covered without guards, you'll get that error.

In this case, you could move the correct case to last and remove the guard.

By on 12/30/2009 2:32 PM ()

Hi, Michael:
I have figured it out. I put a wildcard to catch all others, and it works without warning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> let secretNumber = 33
let guess = 32
match guess with
| _ when guess > secretNumber
  ->
printfn "The secret number is lower."
| _ when guess = secretNumber
  ->
printfn "You've guessed correctly!"
| _ when guess < secretNumber
  ->
printfn "The secret number is higher."
| _ -> printfn "The catch all";;The secret number is higher.val secretNumber : int = 33
val guess : int = 32

But thank you very much for your remind.Happy New Year to you!

By on 12/30/2009 2:46 PM ()

zydjohn, I don't think adding catch-all matches just for the purpose of removing warnings is a good habit to get into.

To the designers of F#: In this case, I think F# should have better support for checking full coverage of this kind of matches. Checking if a numeric variable is within a certain range is a common operation, and checking for full coverage is easy, if the ranges are constant. What about introducing the following patterns?
LessThan x
InRange(first, last)
GreaterThan x

By on 12/31/2009 2:29 AM ()

Hi, Joh:
I agree on what you have said, as in my case, the total searching conditions are not huge. But it is not up to us to ask F# team to add something.
But if Don Syme and his team like to do so, it will be great.
But as VS2010 is approaching its official release, I don't believe they have the time for those "minor" bugs.
Any way, thank you very much for your advice.
Happy New Year to you!

By on 12/31/2009 1:07 PM ()

May I suggest two alternate implementations which avoid the compiler warning without resorting to such a (suspicious) proliferation of wildcards...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let secretNumber = 33
let guess = 32

// simple, direct, procedural version
if guess > secretNumber then
    printfn "The secret number is lower."
elif guess > secretNumber then
    printfn "The secret number is higher."
else
    printfn "You've guessed correctly!"
// fancy, match-based version

let (|LessThan|EqualTo|MoreThan|) c =
    if   c = -1 then LessThan 
    elif c =  1 then MoreThan 
    else EqualTo

match (compare secretNumber guess) with
| LessThan  -> printfn "The secret number is lower."
| EqualTo   -> printfn "You've guessed correctly!"
| MoreThan -> printfn "The secret number is higher."

EDIT: why doesn't the formatting ever work right on this forum?

By on 12/31/2009 7:56 PM ()

@pblasucci:

Your if solution also has a catch-all, in the form of an else block.

I like the fancy solution which uses active patterns. It's limited to comparing to a single number, but I guess it would not be too hard to build variants for pairs and triplets of numbers, which might be enough for most common cases.

By on 1/4/2010 5:05 AM ()

@Joh:

You are correct about the catch-all (else block). I only offered that code because I feel that wildcards (like car horns) should be used judiciously... and a match statement used exclusively for when guards isn't really a match statement at all!

As to the ActivePattern code, that choice of numbers was specific... its the return values from the IComparable.Compare interface, which F# actually uses in its 'compare' library function. So, you're correct, the model could be extended, but I usually like to let the BCL do as much of the leg-work as possible.

Cheers!

By on 1/4/2010 2:15 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper