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
\(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
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
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