John,

>Of course we would all like Haskell to catch as many errors as possible,
and statically. But a chain is only as strong as its weakest link. When
there are already weak links in a chain, it makes no sense to build others
of some hi-tech super strong material. The most efficient design occurs when
all the links are approximately the same strength.
>[...]
>So the question is, with the rest of Haskell as it is, what choice for
>MonadZero most closely matches the rest of the language?
>
>I am more convinced than ever: nuke MonadZero.

I agree with your your premisses, but not with your conclusion. If you take
your reasoning to the extreme (and that's what I like to do :-) the
conclusion is that we only need one class for types of kind *, one for types
of kind * -> *, etc. That is not what we want, I hope.

Let me try to sketch a design methodology for introducing type classes, and
convince you that aside from issues of type safety or the do-notation it is
good to separate Monad and MonadZero. This should also answer Phil's
question wether
there is "anyone out there who values the separation of Monad and MonadZero
for purposes other than `do' notation?".

rule 0: Class introduction

Introduce a class Foo a where { foo :: ...a...} when there are at least two
types X and Y with non-trival and different definitions for fooX :: ...X...
and fooY :: ...Y...., and you want to abstract from X and Y when using foo.
The instances for Foo are instance Foo X where { foo = fooX } and Foo Y
where { foo = fooY }.

When there is just a single type Z for which you can define fooY :: ...Y...
there is no need to abstract over it, and fooY can just as well be
identified with foo.

When for all types a the definitions of fooa are the same, we can just
define foo :: ...a... as a polymorphic function.

It is good style to define non-overloaded versions of class methods outside
of the class instead of inlining them in the instance declaration. For
example the usual instance declaration Eq a => Eq [a] is utterly confusion.
Instead first define eqList :: Eq a => [a] -> [a] -> Bool and then simple
say instance Eq a => Eq [a] where { (==) = eqList }.

It is bad style to have trivial definitions for class methods, in which case
you are lying to the user.

Often there are different implementations for fooX and fooY where Y and X
overlap (e.g. X=[Char] and Y = [a]) but where you don't want to make the
less general one abstract (e.g. I dont want to define newtype String =
String [Char]).

rule 1: Subclass introduction

Introduce a sublass Foo a => Bar a where { bar :: ...a... } when rule 1
tells you to introduce Bar for a proper subset of the instances of Foo.

When all instances of Foo are also instances of Bar, you should add bar to
the methods of Foo.

When there are instances of Bar that are not instances of Foo, Bar should be
a seperate class.

According to these rules Monad and MonadZero should be different classes,
and MonadZero and MonadPlus should be merged. Why Monad? Because we have
{bindId, returnId}, {bindReader, returnReader}, {bindMaybe, returnMaybe},
{bindList, returnList}, {bindIO, returnIO}, {bindST, returnST}. Why
MonadZero? Because we don't have non-trivial definitions for {zeroId},
{zeroReader}, and {zeroST}, but we do have {zeroMaybe}, {zeroList}, and
{zeroIO}. Why merge MonadZero and MonadPlus? Because we have {plusMaybe},
{plusList}, and {plusIO}.

According to these rules we should *not* have Show and Eq as subclasses for
Num.

I think that it is good that recursion, undefined and (less so for) seq are
polymorphic and thus that all Haskell functions are implicitely partial.
According to these rules, there should be no Eval class.

According to these rules, much of the rest of the Numeric class hierarchy
can be justified, and I guess that the original designers used a similar
methodology as I gave.

It is disheartening however, that we want to flatten a hierarchy that is
flat and simple, while it is a taboo to talk about simplifying and
collapsing the complex and deep numeric hierarchy. About 80% of the problems
my students have when doing their programming exercises is with these
classes. We are hunting something tiny and neglect something big.

Erik, Haskell Certified Software Engineer, Meijer



Reply via email to