[Haskell-cafe] programming style...and type classes...

Olaf Klinke olf at aatal-apotheke.de
Sat Nov 5 15:45:55 UTC 2016

While we have stated in this thread what typeclasses should _not_ be used for, we probably do not have carved out yet what `proper' use is. Please help me here.

There are some typeclasses, e.g. Eq, Ord, Num, Monoid, Functor, Monad and Category, that arise from category theory and algebra, so these may be regarded as "good" typeclasses even though a type can have several sensible instances. In that case, newtypes may express this. (Data.Monoid is full of such newtypes.)

In defense of Ord, there are in general many ways to totally order a set (read "set" as "total elements of a type"), and none of these ways can be preferred a priori. I would not say this is an inherently bad property of Ord. Similarly, there are many ways to put a monoid or ring structure on a set. 

Sometimes a type class can be avoided. For example, there is Data.Foldable.toList. Hence, instead of writing a Foldable instance for your type and use a function from the Data.Foldable module, you could instead write your own toList conversion function and then fold that list. (Does anyone know a function involving Foldable that can not be reduced to 'toList' followed by some list operations? Is [] the "free instance" of Fodable?)
However, this reduction may be much more inefficient than using the Foldable instance directly. 

As a contrived example of what I mean by "free instance", consider the following class that abstracts traversal over a sequence of unknown length. 

class ListLike l where
  patternmatch :: l a -> Maybe (a,l a)

You can write ListLike instances for [], Vector, Sequence and Set, but only [a] is the solution to the type equation 

x = Maybe (a,x).

Then there are multi-parameter type classes. This has been described as metaphorical to a type-level Prolog language [1], since ultimately multi-parameter type classes (without methods) are relations between types. 

Another debatable use case: You define many record types, each of which has a sort key component:

data A = A {foo :: Foo, bar :: Bar, keyA :: Int}
data B = B {baz :: Baz, keyB :: Int}
data C = C {... , keyC :: Int}

It might be convenient to define a typeclass

class HasSortKey a where
  key :: a -> Int

and endow the types A, B and C with the obvious instances. But one could also avoid the class by making a parametric type:

data Keyed a = Keyed {payload :: a, key :: Int}

and then let data A = A {foo :: Foo, bar :: Bar} and so forth. Which version would you say is `proper' use?

-- Olaf

[1] https://www.reddit.com/r/haskell/comments/ck459/typed_typelevel_programming_in_haskell_part_i/

More information about the Haskell-Cafe mailing list