Want to keep learning?

This content is taken from the The University of Glasgow's online course, Functional Programming in Haskell: Supercharge Your Coding. Join the course to learn more.
Temple silhouet against sunset sky

Type Classes in more Detail

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
    *Main> 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]
    *Main> ['a'..'z']

Share this article:

This article is from the free online course:

Functional Programming in Haskell: Supercharge Your Coding

The University of Glasgow

Get a taste of this course

Find out what this course is like by previewing some of the course steps before you join: