Classes and Instances
We’ve seen class constraints like Eq a =>
in types. We know how to use
existing classes with existing types. But how do we use existing classes
with our own types? How can we define our own classes?
Here’s how to make your own type a member of the Eq
class:
data Color = Black | White
instance Eq Color where
Black == Black = True
White == White = True
_ == _ = False
A class instance is an instance
block that contains definitions for
the functions in that class. Here we define how ==
works on Color
.
Syntax of Classes and Instances
A type class is defined using class
syntax. The functions in the class
are given types. Here’s a class Size
that contains one function,
size
:
class Size a where
size :: a -> Int
Instances of a class are defined with instance
syntax we’ve just seen.
Here is how we make Int
and [a]
members of the Size
class:
instance Size Int where
size x = abs x
instance Size [a] where
size xs = length xs
Our class Size
behaves just like existing type classes. We can use
size
anywhere where a function can be used, and Haskell can infer
types with Size
constraints for us:
Prelude> :t size
size :: Size a => a -> Int
Prelude> size [True,False]
2
Prelude> let sizeBoth a b = [size a, size b]
Prelude> :t sizeBoth
sizeBoth :: (Size a1, Size a2) => a1 -> a2 -> [Int]
A class can contain multiple functions, and even constants. Here we
define a new version of the Size
class with more content.
class Size a where
empty :: a
size :: a -> Int
sameSize :: a -> a -> Bool
instance Size (Maybe a) where
empty = Nothing
size Nothing = 0
size (Just a) = 1
sameSize x y = size x == size y
instance Size [a] where
empty = []
size xs = length xs
sameSize x y = size x == size y
Sidenote: Restrictions on Instances
The Haskell 2010 allows only a very specific type of class instance.
Let’s look at some instances that aren’t allowed. The examples use the
Size
class:
class Size a where
size :: a -> Int
We saw a Size [a]
instance above. Why not define an instance just for
lists of booleans?
instance Size [Bool] where
size bs = length (filter id bs) -- count Trues
error:
• Illegal instance declaration for ‘Size [Bool]’
(All instance types must be of the form
(T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the
instance head.
Use FlexibleInstances if you want to disable this.)
• In the instance declaration for ‘Size [Bool]’
Dang. As the error tries to tell us, we can only define instances where
all type parameters are different type variables. That is, we can
define instance Size (Either a b)
but we can’t define:
instance Size (Either String a)
– sinceString
is not a type variableinstance Size (Either a a)
– since the type variables aren’t differentinstance Size [[a]]
– since[a]
is not a type variable
Why is this? This rule guarantees that it’s simple for the compiler to look up the correct type class instance. It can just look at what the top-level type constructor is, and then pick an instance.
The GHC Haskell implementation has many extensions to the type class system.
Default Implementations
Did you notice how in the previous example we gave sameSize
the same
definition in both instances? This is a very common occurrence, and it’s
why Haskell classes can have default implementations. As a first
example, here’s an Example
type class for giving example values of
types.
class Example a where
example :: a -- the main example for the type `a`
examples :: [a] -- a short list of examples
examples = [example] -- ...defaulting to the main example
instance Example Int where
example = 1
examples = [0,1,2]
instance Example Bool where
example = True
Here’s how Example
works. Note how the default implementation of
examples
got used in the Bool
case but not in the Int
case. Also
note the need for explicit type signatures to tell GHCi which instance
we’re interested in. Without them, we would get an “Ambiguous type
variable” error.
Prelude> example :: Bool
True
Prelude> example :: Int
1
Prelude> examples :: [Bool]
[True]
Prelude> examples :: [Int]
[0,1,2]
The standard type classes use lots of default implementations to make
implementing the classes easy. Here is the standard definitions for Eq
(formatted for readability).
class Eq a where
(==) :: a -> a -> Bool
x == y = not (x /= y)
(/=) :: a -> a -> Bool
x /= y = not (x == y)
Note how both operations have a default implementation in terms of the
other. This means we could define an Eq
instance with no content at
all, but the resulting functions would just recurse forever. In
practice, we want to define at least one of ==
and /=
.
When there are lots of default implementations, it can be hard to know
which functions you need to implement yourself. For this reason class
documentation usually mentions the minimal complete definition. For
Eq
, the docs
say
“Minimal complete definition: either == or /=.”
Let’s look at Ord
next. Ord
has 7 operations, all with default
implementations in terms of each other. By the way, note the quirky way
of defining multiple type signatures at once. It’s okay, it’s a feature
of Haskell, this is how Ord
is defined in the
standard.
(We’ll get back to what the (Eq a) =>
part means soon.)
class (Eq a) => Ord a where
compare :: a -> a -> Ordering
(<), (<=), (>=), (>) :: a -> a -> Bool
max, min :: a -> a -> a
compare x y | x == y = EQ
| x <= y = LT
| otherwise = GT
x <= y = compare x y /= GT
x < y = compare x y == LT
x >= y = compare x y /= LT
x > y = compare x y == GT
max x y | x <= y = y
| otherwise = x
min x y | x <= y = x
| otherwise = y
With this definition it’s really hard to know what the minimal complete definition is. Luckily the docs tell us “Minimal complete definition: either compare or <=.”
As a final word on default implementations, if there is never a need to
override the default definition, the function can be moved out of the
class for simplicity. Consider a class like Combine
below:
class Combine a where
combine :: a -> a -> a
combine3 :: a -> a -> a -> a
combine3 x y z = combine x (combine y z)
It’s hard to think of a case where combine3
would be given any other
definition, so why not move it out of the class:
class Combine a where
combine :: a -> a -> a
combine3 :: Combine a => a -> a -> a -> a
combine3 x y z = combine x (combine y z)
As an example, here are the Eq
and Ord
instances for a simple pair
type. Note how the definition uses the minimal complete definition rules
by only defining ==
and <=
.
data IntPair = IntPair Int Int
deriving Show
instance Eq IntPair where
IntPair a1 a2 == IntPair b1 b2 = a1==b1 && a2==b2
instance Ord IntPair where
IntPair a1 a2 <= IntPair b1 b2
| a1<b1 = True
| a1>b1 = False
| otherwise = a2<=b2
*Main> (IntPair 1 2) < (IntPair 2 3)
True
*Main> (IntPair 1 2) > (IntPair 2 3)
False
*Main> compare (IntPair 1 2) (IntPair 2 3)
LT
*Main Data.List> sort [IntPair 1 1,IntPair 1 4,IntPair 2 1,IntPair 2 2]
[IntPair 1 1,IntPair 1 4,IntPair 2 1,IntPair 2 2]
Useful Stuff
Deriving
As we’ve seen many times already, deriving
is a way to get
automatically generated class instances. The Read
and Show
classes
should pretty much always be derived to get the standard behaviour. The
derived instance for Eq
is typically what you want. It requires
constructors and fields to match.
The derived Ord
instance might not be what you want. It orders
constructors left-to-right, and then compares fields inside constructors
left-to-right. An example:
data Person = Dead | Alive String Int
deriving (Show, Eq, Ord)
Prelude> Dead < Alive "Bob" 35
True
Prelude> Alive "Barbara" 35 < Alive "Clive" 17
True
Prelude> Alive "Clive" 17 < Alive "Clive" 30
True
Asking GHCi About Classes
You can use the :info
command in GHCi to get the contents and
instances of a class. These days the info even includes the minimal
complete definition (see the MINIMAL pragma). For example:
Prelude> :info Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger,
(negate | (-)) #-}
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Hierarchies
Both classes and instances can form hierarchies. This means that a class or instance depends on another class or instance.
Instance Hierarchies
Let’s try to define an Eq
instance for a simple pair type:
data Pair a = MakePair a a
deriving Show
instance Eq (Pair a) where
(MakePair x y) == (MakePair a b) = x==a && y==b
error:
• No instance for (Eq a) arising from a use of ‘==’
Possible fix: add (Eq a) to the context of the
instance declaration
• In the first argument of ‘(&&)’, namely ‘x == a’
In the expression: x == a && y == b
In an equation for ‘==’:
(MakePair x y) == (MakePair a b) = x == a && y == b
The compiler is trying to tell us that our Eq (Pair a)
instance needs
an Eq a
instance to work. How can we compare pairs of values of type
a
if we can’t compare values of type a
? To solve this we need to add
a type constraint to the instance declaration, just like we’ve added
type constraints to function definitions.
instance Eq a => Eq (Pair a) where
(MakePair x y) == (MakePair a b) = x==a && y==b
Now we can compare pairs, as long as the element type is comparable. However, we can’t compare, say, pairs of functions, since functions don’t have an Eq instance.
Prelude> MakePair 1 1 == MakePair 1 1
True
Prelude> MakePair reverse reverse == MakePair reverse reverse
<interactive>:15:1: error:
• No instance for (Eq ([a0] -> [a0])) arising from
a use of ‘==’ (maybe you haven't applied a function
to enough arguments?)
• In the expression:
MakePair reverse reverse == MakePair reverse reverse
In an equation for ‘it’:
it = MakePair reverse reverse ==
MakePair reverse reverse
Let’s continue with another example. Here’s a simple type class and an instance
class Check a where
check :: a -> Bool
instance Check Int where
check x = x > 0
Now we can write a function that checks a list:
checkAll :: Check a => [a] -> Bool
checkAll = and (map check xs)
In order to turn this into a Check [a]
instance, we need to add a
constraint to the instance declaration. Our Check [a]
is based on the
Check a
instance.
instance Check a => Check [a] where
check xs = and (map check xs)
This means that our Check [a]
instance is only valid when there is a
corresponding Check a
instance. For example, if we try to invoke a
Check [Bool]
instance, we get an error about the missing Check Bool
instance:
Prelude> check [True,False]
<interactive>:1:1: error:
• No instance for (Check Bool) arising from
a use of ‘check’
• In the expression: check [True, False]
In an equation for ‘it’: it = check [True, False]
Also, if we try to define Check [a]
instance without the constraint,
we get an error (with a pretty good suggestion!)
• No instance for (Check a) arising from a use of ‘check’
Possible fix:
add (Check a) to the context of the instance
declaration
If you think about it, this instance hierarchy allows us to circumvent
the limitation that we can’t make a Check [Int]
instance.
Finally, sometimes multiple constraints are needed. Consider for example
the Eq
instance for Either
:
instance (Eq a, Eq b) => Eq (Either a b) where
Left x == Left y = x==y
Right x == Right y = x==y
_ == _ = False
Class Hierarchy
Respectively, a class can depend on another class. This is useful for instance when you want to use functions from another class in your default implementations:
class Size a where
size :: a -> Int
class Size a => SizeBoth a where
sizeBoth :: a -> a -> Int
sizeBoth x y = size x + size y
In cases like this we say SizeBoth
is a subclass of Size
. Note
again the confusion with object oriented programming. Examples of
subclasses in the standard library include:
class Eq a => Ord a where
...
class Num a => Fractional a where
...
Another way to look at subclasses is that if you have a
class Main a => Sub a
, you must provide an instance Main MyType
in
order to be able to declare instance Sub MyType
.
Self Checks
Check 1
What are the functions in the Eq
class?
(==), (/=)
(==)
(==)
,(<)
,(>)
Check 2
For which of the following classes can we get automatic instances with
deriving
?
Num
Ord
Size
Check 3
Which of the following instance declarations is legal?
instance Eq Maybe
instance Eq (a,a)
instance Eq (Maybe Int)
instance Eq (a,b)
Check 4
Given the following definition of the class BitOperations
class BitOperations a where
bitNot :: a -> a
bitNot x = bitNand bitTrue x
bitAnd :: a -> a -> a
bitAnd x y = bitNot (bitOr (bitNot x) (bitNot y))
bitOr :: a -> a -> a
bitOr x y = bitNot (bitAnd (bitNot x) (bitNot y))
bitNand :: a -> a -> a
bitNand x y = bitNot (bitAnd x y)
which set of operations is not a minimal complete definition of
BitOperations
?
bitNand, bitAnd
bitAnd, bitOr
bitAnd, bitNot
bitNot, bitOr
Check 5
The declaration instance Num a => Eq (Pair a)
tells me that
- All instances of
Num
are instances ofEq
Pair a
is an instance ofEq
ifa
is an instance ofNum
- The instance
Eq (Pair a)
inherits the instanceNum a
Check 6
The declaration class Num a => Fractional a
tells me that
- All instances of
Fractional
must be instances ofNum
- All instances of
Num
must be instances ofFractional
- If I define an instance for
Fractional
, I also get an instance forNum
- If I define an instance for
Num
, I also get an instance forFractional
Acknowledgment:
This reading was originally written by Joel Kaasinen and John Lång in their open source textbook. The book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License, which allows users to copy, modify, and distribute the book.
This reading was modified by Titus H. Klinge in 2021 and presented above under the same license in order to better serve the students of this course.