tags:

views:

105

answers:

5

I wonder how people handle nonexhaustive match warnings in the SML/NJ compiler. For example, I may define a datatype

datatype DT = FOO of int | BAR of string

and then have a function that I know only takes FOOs

fun baz (FOO n) = n + 1

The compiler will give a warning

stdIn:1.5-1.24 Warning: match nonexhaustive
          FOO n => ...
val baz = fn : DT -> int

I don't wanna see warnings for incomplete matches I did on purpose, because then I have to scan through the output to find a warning that might actually be a bug. I can write the function like this

fun baz (FOO n) = n + 1 
  | baz _ = raise Fail "baz"

but this clutters the code. What do people usually do in this situation?

+1  A: 

You've got to either cover all the cases so that you've decided how the function handles the entirety of its domain, or live with the warning. The final alternative is to revise what values you feed through the function in the first place so that the deconstruction is done in the caller.

Donal Fellows
+2  A: 

You can set the following compiler flags to configure the warning level of non-exhaustive match warnings:

  • Compiler.Control.MC.matchNonExhaustiveWarn
  • Compiler.Control.MC.matchNonExhaustiveError

If both of these are set to false, there will be no warning generated. Unfortunately, this will turn off warnings for all instances of this error, which is probably not what you want since it will remove this safeguard.

(Note: You just set these to false in your code)

More info can be found here. Item 46 describes your warning specifically.

Daniel Brotherston
+1  A: 

As Daniel mentioned, you can turn off the warning, but I would not recommend that.

It is best if you can adjust your data types so that functions are able to operate over the entire range of values allowed. Second best is to go ahead and "clutter" the code with errors to make it explicit what's happening (and allow more meaningful run-time errors).

Michael E
A: 

I think if you do this a lot you need to rethink your datatypes a bit. Why are you grouping Foo and Baz together in one datatype if they are not the same sort of object? If they are different ways of constructing the same object, then you would expect that a function which works on Foo would be able to do something sensible on Baz as well. If you have a type Vehicle with constructors Car and Bike, but you only want to do some operation on Cars, if your code is large the right thing to do might to separate out the definition of Car and change all the places where you are using Vehicles but expecting only Car to use Car directly.

Nicholas Wilson
A: 

Like the other answers say, it's cleaner to change your datatypes to produce an exhaustive match. In this case, you might change the DT type, or you could change the return type from baz. For example:

datatype DT = FOO of int | BAR of string
fun baz (FOO n) = SOME (n + 1)
  | baz _       = NONE

Then baz has the type val baz = fn : DT -> int option and you don't have to worry about handling errors raised by baz.

snim2