I'm not sure I'd want -Wall on by default (though being -Wall clean is
very good). But exhaustive pattern checking might well help out a lot of
people coming from untyped backgrounds.

   http://ocaml.janestreet.com/?q=node/64

Ron's also wondering why exhaustive pattern checking isn't on ?

Anyone know why it isn't the default?

The answer to the latter is probably just that it is imprecise. Don't
read on if that's all you wanted to hear;-)

But before anyone goes suggesting to change the defaults, consider that it would be solving the wrong problem

- exhaustiveness of Haskell matching is not decidable, so you end up
   with an approximation; if you want to enforce this particular
   approximation, you end up with "defensive programming"

- defensive programming may look good in blame-driven development
   methods, but is actually bad for fault-tolerance (google for "Erlang
   defensive programming")

- if you are serious about correctness, rather than the appearance of
   correctness, there are other approaches:

   - non-defensive programming with external recovery management
       is the "industry-standard" (well, in Erlang industries, at least;-);

       the idea being that trying to handle all cases everywhere makes
       for unreadable code that doesn't address the problem, while the
       combination of checking cases on entry/handling the expected
       case locally/handling unexpected cases non-locally has been
       demonstrated to work in high-reliability scenarios.

   - checking for non-crashing rather than syntactic coverage is slightly
       better, but will likely only demonstrate that there are cases where
you can't put in a useful answer locally;
       you can add local information to the error before raising it (which
would still be reported as a crash), but trying to handle a case locally (just to avoid the local crash) when you don't have the information to do so is just going to hide bugs (which is not what a coding style or warning should encourage);
       the way forward, if you believe Erlangers, is to embrace localised
       crashes (google for Erlang motto: "let it crash";-), and have the
       infrastructure in place to automatically add useful information to
crash messages [1], to programatically deal with crashes (preferably in a separate process on an independent machine), and to factor
       out the recovery code from the business-as-usual code (which is
       why Erlang programs with good fault-tolerance characteristics
are often shorter than non-Erlang programs without).
       The added benefit is that when a crash happens, in spite of serious
efforts spent on proving/testing/model checking, the system doesn't just go "oops!".

- if you want coverage to have value semantically, you need to restrict the expressiveness of matching. In Haskell/OCaml, that would probably mean extensible variants/records, or possibly GADTs, (and no guards), so that you get a type error when *using* a value that is not covered (instead of a syntax error when not covering a value that may not ever be used).

       In particular, if you really care about such things, you should
       check for the good cases once, then convert to types that do
       not have the bad cases. That still doesn't help you with the entry
       check, where the best option for the unexpected case might still
be to raise an exception, but such a typing strategy makes exhaustiveness checking slightly more useful.

In management brief: enforcing exhaustiveness testing without any other measures just ensures that your team members will never tell
you about things they do not know how to handle, so the first sign
of trouble you see is when everything comes down. Establishing a
non-local framework for dealing with local non-exhaustiveness and
encouraging team members to raise alarms when they see trouble
gives your team a chance to notice and recover from issues before
they become critical.

Perhaps syntactic exhaustiveness testing should be renamed to
certified-everything-swept-under-the-rug testing?-)

Note: this is no excuse for using things like 'head' unguardedly in
a large code base! Just that the solution is not to add an empty
case to 'head', but to ensure that 'head' is never called with an
empty list, so "exhaustiveness everywhere" is the wrong target. Adding an empty case to 'head' is to raise an alert with useful info, working around current weaknesses in the error-reporting infrastructure [1], after we've already run into an unhandled unexpected case.

If we get a "non-exhaustive" warning, it might mean a variety
of things (you might want to document things like 3/4 in the
types):

1 nothing (error in exhaustiveness checking)
2 nothing (approximation in exhaustiveness checking)
3 nothing (missing cases are proven never to arrive here)
4 nothing (exceptional cases crash here, are dealt with properly
   elsewhere)
5 wrong type used (you shouldn't even try to make this match
   exhaustive, you should use a type that encodes only the
   cases you handle)

beside the naive expectation

- missing case (need to cover the case)

These warnings need to be used with care, and with a conscious choice of strategy/framework, not by default. If
you have a good strategy/framework in place, switch them
on, even make them errors, but doing so by default would
only encourage bad coding practices.

Just another opinion (should I've put it on a blog instead?-)
Claus

"Did you want to talk about the weather or were you just
making chit-chat?" Weather man, Groundhog Day

[1] http://hackage.haskell.org/trac/ghc/wiki/ExplicitCallStack


_______________________________________________
Glasgow-haskell-users mailing list
Glasgow-haskell-users@haskell.org
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

Reply via email to