I’m having a simple problem when it comes to writing up typeclasses that inherit from one another. I’m trying to create a hierarchy of typeclasses to achieve some level of representational abstraction. Let’s say I want a collections typeclass:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class Collection c a where
isMember :: a -> c -> Bool
And I’ve defined a tree type:
data Tree a = Empty | Node a (Tree a) (Tree a)
deriving (Show, Eq)
I’d like to make my tree a collection, so:
inOrder :: Tree a -> [a]
inOrder Empty = []
inOrder (Node a l r) = (inOrder l) ++ [a] ++ (inOrder r)
instance (Eq a) => Collection (Tree a) a where
isMember a c = a `elem` (inOrder c)
This doesn’t quite work right:
*Main> (isMember '2' Empty)
<interactive>:1:1:
No instance for (Collection (Tree a) Char)
arising from a use of `isMember' at <interactive>:1:1-18
Possible fix:
add an instance declaration for (Collection (Tree a) Char)
In the expression: (isMember '2' Empty)
In the definition of `it': it = (isMember '2' Empty)
Presumably the value of the typeclass would be lost if I had to create an implementation for each concrete type. So I’m not writing the instance declaration correctly. But I can’t quite figure out how to proceed.
The issue here is that, by default, each type class parameter is independent of the others. Simply applying
isMemberto aCharand a tree of unknown element type isn’t enough to let it deduce that it should use the (seemingly obvious) instance.This is clearly not how you want it to work, and a bit silly to boot, but that’s how it works. To deal with the problem, you need to give it some way of making the connection, for which there are other extensions:
FunctionalDependenciesis the more common, whileTypeFamiliesis newer and much nicer to use, but still has some rough edges.With functional dependencies, you would specify that some subset of the type class parameters fully determine others, e.g.
Collection c a | c -> a. This has its own pitfalls, but works well enough in many cases.With type families, you would probably reduce this to a single-parameter type class, with the element type associated, e.g.: