The blog post introduces an important concept of typeclasses in Haskell.

Typeclasses
We have been talking extensively about types, but we have avoided using Num
, Ord
, and Eq
that VSCode often suggests.
This is because they are typeclasses, not types, and I wanted to discuss typeclasses in a separate article. Typeclasses
are like interfaces, if you are familiar with object-oriented programming, which define some behavior of types that belong
to that typeclass. The +
function is an example of a behavior enforced by the Num
typeclass.
ghci> :t (+)
(+) :: Num a => a -> a -> a
The type of the +
function indicates that any type in the Num
typeclass could be inputs and an output.
Using the :info
flag, we can examine other functions defined in a typeclass.
ghci> :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 | (-)) #-}
-- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Word -- Defined in ‘GHC.Num’
Notice here that only the types are defined for those functions.
This is because the actual logic for different types that belong to this typeclass might be different.
The part where it says MINIMAL
highlights the functions that are necessary to make a valid instance of that typeclass.
The next part lists some predefined types that belong to the Num
typeclass. Let's see the typeclasses that are notoriously used.
ghci> :info Show
class Show a where
showPrec :: Int -> a -> ShowS
show :: a -> String
showList :: [a] -> ShowS
{-# MINIMAL ShowPrec | show #-}
ghci> :info Eq
class Eq a where
(==) :: a -> Bool
(/=) :: a -> Bool
{-# MINIMAL (==) | (/=) #-}
Most types belong to Show
so that the output of a function can be presented as a string in GHCi, and Eq
is responsible for testing equality.
Usage
When does this become useful? Let's look at how typeclasses can be used in an example of a Temperature
class.
data Temperature = C Float | F Float
--- The C and F constructors are for Celsius and Fahrenheit respectively
--- use Eq to enforce rules
instance Eq Temperature where
(==) (C n) (C m) = n == m
(==) (F n) (F m) = n == m
(==) (C n) (F m) = (1.8*n - 3.2) == m
(==) (F n) (C m) = (1.8*m - 3.2) == n
--- Pattern matching
We can see that by using instance Eq and defining logic for the Temperature
datatype,
we can check for equality even when we have different temperature scales (Celsius vs Fahrenheit).
Deriving Typeclasses
If you do not want to have special rules for a datatype like Temperature
and just want to make the
datatype be an instance of Eq
for a simple equality check, we can use the following shorter syntax.
data Temperature = C Float | F Float
deriving(Eq)
When you use the deriving
syntax, Haskell can automatically generate the simplest functions necessary for it to be a valid
instance of the specified typeclass(es). Though the above is easy and works for the most part, there are cases where you
might want to avoid using this syntax. Let's see what Haskell automatically generated for Temperature
from the above.
(==) (C n) (C m) = n == m
(==) (F n) (F m) = n == m
(==) _ _ = False
We can see that Haskell only considered the cases where value constructors are the same because it does not know the semantic meaning of the symbols of value constructors.
Exercises
This is an exercise section where you can test your understanding of the material introduced in the article. I highly recommend solving these questions by yourself after reading the main part of the article. You can click on each question to see its answer.
Resources
- Philipp, Hagenlocher. 2020. Haskell for Imperative Programmers #13 - Typeclasses. YouTube.
- NA. Making Our Own Types and Typeclasses. Learn You a Haskell for Great Good.