views:

407

answers:

4

In Erlang, you are encouraged not to match patterns that you do not actually handle. For example:

case (anint rem 10) of
    1 -> {ok, 10}
    9 -> {ok, 25}
end;

is a style that is encouraged, with other possible results resulting in a badmatch result. This is consistant with the "let it crash" philosophy in Erlang.

On the other hand, F# would issue an "incomplete pattern matching" in the equivalent F# code, like here.

The question: why wouldn't F# remove the warning, effectively by augmenting every pattern matching with a statement equivalent to

|_ -> failwith "badmatch"

and use the "let it crash" philosophy?

Edit: Two interesting answers so far: either to avoid bugs that are likely when not handling all cases of an algebraic datatype; or because of the .Net platform. One way to find out which is to check OCaml. So, what is the default behaviour in OCaml?

Edit: To remove misunderstanding by .Net people who have no background in Erlang. The point of the Erlang philosophy is not to produce bad code that always crashes. Let it crash means let some other process fix the error. Instead of writing the function so that it can handle all possible cases, let the caller (for example) handle the bad cases which are thrown automatically. For those with Java background, it is like the difference between having a language with checked exceptions which must declare everything it will possibly return with every possible exception, and having a language in which functions may raise exceptions that are not explicitly declared.

+12  A: 

In most cases, particularly with algebraic datatypes, forgetting a case is likely to be an accident and not an intentional decision to ignore a case. In strongly typed functional languages, I think that most functions will be total, and should therefore handle every case. Even for partial functions, it's often ideal to throw a specific exception rather than to use a generic pattern matching failure (e.g. List.head throws an ArgumentException when given an empty list).

Thus, I think that it generally makes sense for the compiler to warn the developer. If you don't like this behavior, you can either add a catch-all pattern which itself throws an exception, or turn off or ignore the warning.

kvb
+13  A: 

F# (and other languages with pattern matching, like Haskell and O'Caml) does implicitly add a case that throws an exception.

In my opinion the most valuable reason for having complete pattern matches and paying attention to the warning, is that it makes it easy to refactor by extending your datatype, because the compiler will then warn you about code you haven't yet updated with the new case.

On the other hand, sometimes there genuinely are cases that should be left out, and then it's annoying to have to put in a catch-all case with what is often a poor error message. So it's a trade-off.

In answer to your edit, this is also a warning by default in O'Caml (and in Haskell with -Wall).

Ganesh Sittampalam
Thanks. I can see refactoring is a good reason. And thanks for the OCaml/Haskell info.
Muhammad Alkarouri
+5  A: 

why wouldn't F# remove the warning

Interesting that you would ask this. Silently injecting sources of run-time error is absolutely against the philosophy behind F# and its relatives. It is considered to be a grotesque abomination. This family of languages are all about static checking, to the extent that the type system was fundamentally designed to facilitate exactly these kinds of static checks.

This stark difference in philosophy is precisely why F# and Python are so rarely compared and contrasted. "Never the twain shall meet" as they say.

So, what is the default behaviour in OCaml?

Same as F#: exhaustiveness and redundancy of pattern matches is checked at compile time and a warning is issued if a match is found to be suspect. Idiomatic style is also the same: you are expected to write your code such that these warnings do not appear.

This behaviour has nothing to do with .NET and, in fact, this functionality (from OCaml) was only implemented properly in F# quite recently.

For example, if you use a pattern in a let binding to extract the first element of a list because you know the list will always have at least one element:

let x::_ = myList

In this family of languages, that is almost always indicative of a design flaw. The correct solution is to represent your non-empty list using a type that makes it impossible to represent the empty list. Static type checking then proves that your list cannot be empty and, therefore, guarantees that this source of run-time errors has been completely eliminated from your code.

For example, you can represent a non-empty list as a tuple containing the head and the tail list. Your pattern match then becomes:

let x, _ = myList

This is exhaustive so the compiler is happy and does not issue a warning. This code cannot go wrong at run-time.

I became an advocate of this technique back in 2004, when I refactored about 1kLOC of commercial OCaml code that had been a major source of run-time errors in an application (even though they were explicit in the form of catch-all match cases that raised exceptions). My refactoring removed all of the sources of run-time errors from most the code. The reliability of the entire application improved enormously. Moreover, we had wasted weeks hunting bugs via debugging but my refactoring was completed within 2 days. So this technique really does pay dividends in the real world.

Jon Harrop
Nitpick: if the behaviour of the code changed, you weren't refactoring.
Douglas
@Jon: you got stuck in the Python issue, I see. See my comment to JSBands in Justin Niessner's answer for why the Erlang philosophy is not so bad. Erlang has much more stable applications in telecom than anything .Net has achieved so far. Try to understand the other side of the question before answering. Let it crash translates in Erlang to let some other process fix the error. I refer you to [Joe Armstrong's thesis](http://www.erlang.org/download/armstrong_thesis_2003.pdf) for more details.
Muhammad Alkarouri
Funny, "Silently injecting sources of run-time error" is exactly why Erlang doesn't force you to add things like `| _ -> ()` just to get rid of them. Erlang will always return `badmatch` if you don't match one of the given cases.
Muhammad Alkarouri
@Jon: I ask questions because I am interested in answers. My questions are not necessarily for you, and I can't see why you want to turn everything into a personal issue. You want to prove that I am a Python guy that does not belong in the high echelon of real programmers? Is that it? Why would every answer of yours start by an attack on the OP and end by a commercial?
Muhammad Alkarouri
@Douglas: The behaviour of the code is not changed.
Jon Harrop
@Muhammad: I am neither attacking you nor advertising anything. I am telling you about the difference in philosophy because I think you will find it interesting, not least because it underpins many of your recent questions. This philosophy has nothing to do with .NET, it originates from the ML family of languages that were bred for theorem proving before Erlang existed. That can be thought of as an extreme form of static type checking. Hence their obsession with trying to statically type check as much as possible.
Jon Harrop
+2  A: 

OCaml by default does warn you about incomplete matches. You can disable it by adding "p" to the "-w" flag. The idea with these warnings is that more often than not (at least in my experience) they are an indication of programmer error. Especially when all your patterns are really complex like Node (Node (Leaf 4) x) (Node y (Node Empty _)), it is easy to miss a case. When the programmer is sure that it cannot go wrong, explicitly adding a | _ -> assert false case is an unambiguous way to indicate that.

GHC by default turns off these warnings; but you can enable it with -fwarn-incomplete-patterns

newacct