Haskell @ Club De Science - TypeClasses



> module TypeClasses where

Type Classes

A type class is an interface that defines some behavior.

We saw the Show type class that contains one method show that allows printing of expressions:

class Show a where 
  show :: a -> String

The function show is special because it is used by ghci to print the result of our computations.

Another commonly used class is the one that defined equality:

class Eq a where
 (==) :: a -> a -> Bool
 (/=) :: a -> a -> Bool

The above class definition defines two methods, equality (==) and inequality (/=). _Question:_What is the type of the equality operator (==)?

Type Class Constraints

The type of a class method (here, the equality operator (==)) is

(==) :: (Eq a) -> a -> a -> Bool

This means that equality can be used to any type that satisfies the type class constraint Eq, i.e., any type that instantiates the class Eq.

Haskell has many build-in instances of Eq, for example, Integers are one such instance. Thus, checking equality of integers is OK:

1 == 3 = False

Constraint Propagation

Type class constraints are propagated. Thus any function that uses equality check, also has the equality type class constraint:

> checkEq :: (Eq a) => a -> a -> String
> checkEq x y = if x == y then "Equal" else "Not Equal" 

Type Class Instances

Remember our old friend, the Err data type?

> data Err a = Value a | Error a deriving (Show)

If we try to show an Err value, we will get an awful type error

    No instance for (Show (Err a0)) arising from a use of ‘show’

To fix this error we need to make Err an instance of the class Show. There are are two ways to go.

data Err a = Value a | Error a deriving (Show)

Haskell has a mechanism to derive instances for many commonly used classed, including Eq.

instance Show a => Show (Err a) where
  show (Value x) = show x
  show (Error x) = "Error!" ++ show x

Note that the above definition required the types a to already instantiate Show. That is why the first line has an implication =>.

Exercise: Make an Err a an instance of the Eq class, so that the following code is OK

Define two Err values, z1 and z2

> z1 = Error "foo"
> z2 = Error "bar"

And then compare them in ghci:

z1 == z2
checkEq (Error 42) (Error 24)
> instance Eq a => Eq (Err a) where
>   (Error x1) == (Error x2) = True
>   (Value x1) == (Value x2) = x1 == x2 
>   (Error x1) == (Value x2) = False
>   (Value x1) == (Error x2) = False