Lecture 13-2
Type Classes
Some functions are polymorphic, but can’t be applied to any type. +
is a good example. It can work on float
and integer
but not on mixed types or Char
. The type of +
is:
(+) :: Num a => a -> a -> a
This means that a
is a number in the definition: a -> a -> a
.
Num
Num
is a type class:
- It restricts the type variable
a
to only be number types. Word
,Int
,Integer
,Float
,Double
are all contained inNum
.Char
,Bool
, tuples and lists are not inNum
.
Num
Sub-Classes
Num
has two sub-classes.
Integral
represents whole numbers and contains:
Word
(un-signed integers)Int
Integer
Fractional
represents rationals and contains:
Float
Double
Rational
Eq
The types in the class Eq
are the types which can be compared with the function ==
. There are no default types which aren’t equality testable but if you make your own type then you will have to add it to this class.
Type Class Syntax
The syntax of a class type is:
[Type class 1], [Type class 2], ...) => [Type]
This results in a declaration similar to the following:
equals_two :: (Eq a, Num a) => a -> a -> Bool
equals_two a b = a + b == 2
This type declaration also means that if you use functions like ==
in your function then you must specify that the type is comparable with Eq a
. If you don’t then the compiler will give an error.
The Most General Type Annotation
The most general type annotation is the one that is least restrictive. Ideally you want your type annotation to be the most general without giving an error:
equals_two a b = a + b == 2
-- Too restrictive
equals_two :: Int -> Int -> Bool
-- Most general
equals_two :: (Eq a, Num a) => a -> a -> Bool
-- Too general (will give error)
equals_two :: a -> a -> Bool
The most general type annotation is the one which allows the most valid types
Numbers
In Haskell numbers, such as 10
have a polymorphic type:
10 :: Num p => p
This means that, unless stated explicitly that numbers will be converted to the most suitable type in the class of Num
.
You can force a number to be a particular type by using the ::
operator:
1 :: Integer
> 1
1 :: Float
> 1.0
The function fromIntegral
will take any Integral
type and convert it into a Num
type. This, for example would allow you to use the /
operator on an Integer
:
fromIntegral (1 :: Int) / 2
> 0.5
Converting Floats to Integers
Converting floats to integers is a lossy operation. This means that you shoul choose how to complete the rounding:
ceiling 1.6
> 2
floor 1.6
> 1
truncate 1.6
> 1
round 1.6
> 2
Other Type Classes
There are many other type classes in Haskell that won’t be covered:
length :: Foldable t => t a -> Int
This that a
should be accepted if it is a list.
If you see any of the following as a types:
- Functor
- Foldable
- Traversable
then think list.
Exercises
square_area :: Num a => a -> a -> a
triangle_area :: Fractional a => a -> a -> a
equal_heads :: Eq a => [a] -> [a] -> Bool