Typeclasses - Eq
2018-11-03
Let's think about horses. There are three kinds of Horse:
data Horse = SmallHorse | LargeHorse | OtherHorse
Let's make a function to check whether two Horses are in fact equivalently sized.
isSameHorse :: Horse -> Horse -> Bool
isSameHorse first second = first == second
Looks like a classic. Let's run it!
isSameHorse SmallHorse LargeHorse
Shit!
• No instance for (Eq Horse) arising from a use of ‘==’
• In the expression: first == second
In an equation for ‘isSameHorse’:
isSameHorse first second = first == second
That's terrible news. What's wrong here? Apparently, we need to make an instance of the Eq
(short for 'equality') typeclass for Horse
before they can be compared. What's the Eq
typeclass?
We can find out more by firing up ghci
, the GHC repl.
You should see a prompt with the following:
Prelude>
If we enter :info Eq
, we get the following:
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
{-# MINIMAL (==) | (/=) #-}
It shows there are two functions in the Eq
typeclass, ==
and /=
(equals and not-equals), and that a "minimal" definition of Eq
only needs one of those.
Let's start again and make a better horse.
data BetterHorse = Tiny | Average | Huge
Let's not make the same mistake this time - let's make an instance of the Eq
typeclass for them. We are going to implement ==
which has a type of a -> a -> Bool
.
instance Eq BetterHorse where
Tiny == Tiny = True
Average == Average = True
Huge == Huge = True
_ == _ = False
OK, seems fine. We've listed all the times two BetterHorse
are the same and used _ == _ = False
to mean "anything else is not equal" to save ourselves listing every alternative.
isSameBetterHorse :: BetterHorse -> BetterHorse -> Bool
isSameBetterHorse first second = first == second
Now our BetterHorse
comparing function works. Let's give it a go.
nope :: Bool
nope = isSameBetterHorse Tiny Huge
-- nope = False
yep :: Bool
yep = isSameBetterHorse Average Average
-- yep = True
All seems to be fine here. We even get the /=
function for free by defining ==
.
nice :: Bool
nice = Average /= Tiny
-- nice = True
If you're thinking "this seems laborious", you'd be right. Fortunately, for basic data types like this, we can simply auto-generate an Eq
instance in the data definition like this:
data LazyHorse = LazyTiny | LazyOther deriving (Eq)
workingNow = LazyTiny == LazyTiny
-- workingNow == True
Great!
Make sense? If not, why not get in touch?
Further reading: