Skip main navigation

Get 30% off one whole year of Unlimited learning. Subscribe for just £249.99 £174.99. T&Cs apply

Type Classes in more Detail

In this article we take a closer look at type classes
Temple silhouet against sunset sky
© Wim Vanderbauwhede

Type class and instance declarations

Defining type classes

  • A type class is a set of types for which some operations are defined.

  • Haskell has some standard type classes that are defined in the Standard Prelude.

  • You can also define your own.

A type for bright colors

Suppose we’re computing with colors. Here’s a type, and a couple of functions.

 data Bright
= Blue
| Red
deriving (Read, Show)

darkBright :: Bright -> Bool
darkBright Blue = True
darkBright Red = False

lightenBright :: Bright -> Bright
lightenBright Blue = Red
lightenBright Red = Red

A type for milder colors

Now, suppose we have a different type that needs similar functions.


data Pastel
= Turquoise
| Tan
deriving (Read, Show)

darkPastel :: Pastel -> Bool
darkPastel Turquoise = True
darkPastel Tan = False

lightenPastel :: Pastel -> Pastel
lightenPastel Turquoise = Tan
lightenPastel Tan = Tan

Defining a type class

  • Both of our color types have functions to decide whether it’s dark,
    or to lighten it.

  • We can define a class (Color) and its corresponding functions.


class Color a where
dark :: a -> Bool
lighten :: a -> a

This says

  • (Color) is a type class

  • The type variable (a) stands for a particular type that is in the
    class (Color)

  • For any type (a) in (Color), there are two functions you can use:
    (dark) and (lighten), with the specified types.

Defining instances for the type class

  • An (instance) declaration says that a type is a member of a type
    class.

  • When you declare an instance, you need to define the class
    functions.

  • The following says that the type (Bright) is in the class (Color),
    and for that instance, the (dark) function is actually (darkBright).


instance Color Bright where
dark = darkBright
lighten = lightenBright
  • Similarly, we can declare that (Pastel) is in (Color), but it has
    different functions to implement the class operations.

instance Color Pastel where
dark = darkPastel
lighten = lightenPastel

Predefined type classes

Haskell provides several standard type classes. We have a look at two of them: (Num) and (Show).

The Num class

  • (Num) is the class of numeric types.
  • Here is (part of) its class declaration:
 class Num a where
(+), (-), (*) :: a -> a -> a

Num instances

  • There are many numeric types; two of them are (Int) and (Double).

  • There are primitive monomorphic functions that perform arithmetic on
    these types (these aren’t the real names):

addInt, subInt, mulInt :: Int -> Int -> Int
addDbl, subDbl, mulDbl :: Double -> Double -> Double

instance Num Int where
(+) = addInt
(-) = subInt
(*) = mulInt

instance Num Double where
(+) = addDbl
(-) = subDbl
(*) = mulDbl

Hierarchy of numeric classes

  • There are some operations (addition) that are valid for all numeric
    types.

  • There are some others (e.g. trigonometric functions) that are valid
    only for some numeric types.

  • Therefore there is a rich hierarchy of subclasses, including

    • (Integral) — class of numeric types that represent integer
      values, including (Int), (Integer), and more.

    • (Fractional) — class of types that can represent fractions.

    • (Floating) — class containing (Float), (Double), etc.

    • (Bounded) — class of numeric types that have a minimal and
      maximal element.

    • (Bits) — class of types where you can access the representation
      as a sequence of bits, useful for systems programming and
      digital circuit design.

  • If you want to get deeply into numeric classes and types, refer to
    the Haskell documentation.

The Show class

  • We have been using (show) to convert a data value to a string, which
    can then be written to output.

  • Some values can be “shown”, but not all.

  • For example, it is impossible in general to show a function.

  • Therefore (show) needs a type class!

  • [show :: Show, a Rightarrow a rightarrow ,String]

Defining your own Show instance


data Foo = Bar | Baz

We might want our own peculiar string representation:


instance Show Foo where
show Bar = "it is a bar"
show Baz = "this is a baz"

Recall that when you enter an expression (exp) into ghci, it prints
(show exp). So we can try out our strange instance declaration:


*Main> Bar
it is a bar
*Main> Baz
this is a baz

Deriving Show

This is a similar type, but it has a (deriving) clause.


data Foo2 = Bar2 | Baz2
deriving (Read, Show)

Haskell will automatically define an instance of (show) for (Foo2),
using the obvious definition:


*Main> Bar2
Bar2
*Main> Baz2
Baz2

More standard typeclasses

Here is a summary of some of the type classes defined in the standard
libraries.

  • (Num) — numbers, with many subclasses for specific kinds of number.

  • (Read) — types that can be “read in from” a string.

  • (Show) — types that can be “shown to” a string.

  • (Eq) — types for which the equality operator (==) is defined.

  • (Ord) — types for which you can do comparisons like (<), (>), etc.

  • (Enum) — types where the values can be enumerated in sequence; this
    is used for example in the notation ([1..n]) and (‘a’..’z’).


*Main> [1..10]
[1,2,3,4,5,6,7,8,9,10]
*Main> ['a'..'z']
"abcdefghijklmnopqrstuvwxyz"
© University of Glasgow
This article is from the free online

Functional Programming in Haskell: Supercharge Your Coding

Created by
FutureLearn - Learning For Life