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 ...
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 adefault
, because the compiler will tell you if you add new elements to the enumeration and you forgot to handle them.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.The Haskell prelude (standard functions) contains many partial functions, e.g. head and tail only work on non-empty lists, but don't ask me why.
This question has two aspects.
As the two aspects contradict each other, the right answer depends on the circumstances (see also kvb's answer). A solution which is 100% "correct" from any point of view would have to
Example:
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.
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
In which case the answer is YES!