views:

615

answers:

6

Is it generally considered a bad practice to use non-exhaustive pattern machings in functional languages like Haskell or F#, which means that the cases specified don't cover all possible input cases?

In particular, should I allow code to fail with a MatchFailureException etc. or should I always cover all cases and explicitly throw an error if necessary?

Example:

let head (x::xs) = x

Or

let head list = 
    match list with
    | x::xs -> x
    |    _  -> failwith "Applying head to an empty list"

F# (unlike Haskell) gives a warning for the first code, since the []-case is not covered, but can I ignore it without breaking functional style conventions for the sake of succinctness? A MatchFailure does state the problem quite well after all ...

+27  A: 

If you complete your pattern-matchings with a constructor [] and not the catch-all _, the compiler will have a chance to tell you to look again at the function with a warning the day someone adds a third constructor to lists.

My colleagues and I, working on a large OCaml project (200,000+ lines), force ourselves to avoid partial pattern-matching warnings (even if that means writing | ... -> assert false from time to time) and to avoid so-called "fragile pattern-matchings" (pattern matchings written in such a way that the addition of a constructor may not be detected) too. We consider that the maintainability benefits.

Pascal Cuoq
Ah, I haven't thought of fragile matchings - This is indeed a point +1
Dario
+5  A: 

Explicit is better than implicit (borrowed from the Zen of Python ;))

It's exactly the same as in a C switch over an enum... It's better to write all the cases (with a fall through) rather than just putting a default, because the compiler will tell you if you add new elements to the enumeration and you forgot to handle them.

fortran
+4  A: 

I think that it depends quite a bit on the context. Are you trying to write robust, easy to debug code, or are you trying to write something simple and succinct?

If I were working on a long term project with multiple developers, I'd put in the assert to give a more useful error message. I also agree with Pascal's comment that not using a wildcard would be ideal from a software engineering perspective.

If I were working on a smaller scale project on which I was the only developer, I wouldn't think twice about using an incomplete match. If necessary, you can always check the compiler warnings.

I think it also depends a bit on the types you're matching against. Realistically, no extra union cases will be added to the list type, so you don't need to worry about fragile matching. On the other hand, in code that you control and are actively working on, there may well be types which are in flux and have additional union cases added, which means that protecting against fragile matching may be worth it.

kvb
A: 

The Haskell prelude (standard functions) contain many partial functions, e.g. head and tail only work on non-empty lists, but don't ask me why.

Volvic
+2  A: 

This is a special case of a more general question, which is "should you ever create partial functions". Incomplete pattern matches are only one example of partial functions.

As a rule total functions are preferable. When you find yourself looking at a function that just has to be partial, ask yourself if you can solve the problem in the type system first. Sometimes that is more trouble than its worth (e.g. creating a whole type of lists with known lengths just to avoid the "head []" problem). So its a trade-off.

Or maybe you just asking whether its good practice in partial functions to say things like

head [] = error "head: empty list"

In which case the answer is YES!

Paul Johnson
+1  A: 

Non-exhaustive pattern machings are idiomatic in Haskell but extremely bad style in F# (and OCaml, Standard ML etc.).

Exhaustiveness checking is a very valuable way to catch errors at compile time in strict languages like F# but lazy languages like Haskell often use infinite lazy lists where the empty list cannot arise so they (historically) chose brevity over statically-checked correctness.

Jon Harrop
I don't get it. Why do you think non-exhaustive pattern machings are idiomatic in Haskell? And just because it's lazy, doesn't mean empty lists cannot arise.
Wei Hu
Real Haskell code often uses non-exhaustive pattern matchings. Haskell compilers do not warn about non-exhaustive pattern matchings by default. This improves brevity in Haskell code because you don't have to add match cases to handle the empty list in functions that will only ever deal with infinite lazy lists. Laziness is relevant because you can only have infinite lists if they are lazy and not if they are strict (because they would require infinite memory).
Jon Harrop
It's rare to write code that will *only* deal with infinite lists.
Ganesh Sittampalam