Part 18

Standard Type Classes

Here are some standard Haskell type classes you should know about.

Eq

We already saw the Eq class for equality comparisons. Here are the basic operations of the Eq class and some examples of their use. As you can see pretty much all the types we’ve seen so far, except for functions, are members of Eq.

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

Prelude> 1 == 2
False
Prelude> 1 /= 2
True
Prelude> "Foo" == "Bar"
False
Prelude> [[1,2],[3,4]] == [[1,2],[3,4]]
True
Prelude> (\x -> x+1) == (\x -> x+2)

<interactive>:5:1: error:No instance for (Eq (Integer -> Integer))
        arising from a use of==(maybe you haven't applied a function to enough arguments?)In the expression: (\ x -> x + 1) == (\ x -> x + 2)
        In an equation forit: it = (\ x -> x + 1) == (\ x -> x + 2)

There are some other useful functions that use the Eq class, like nub from the module Data.List.

Prelude> import Data.List
Prelude Data.List> :t nub
nub :: Eq a => [a] -> [a]
Prelude Data.List> nub [3,5,3,1,1]      -- eliminates duplicates
[3,5,1]

Ord

The Ord class is for ordering (less than, greater than). Again, here are the basic operations and some examples of their use. Note the new Ordering type. It has values LT for “less than”, EQ for “equal” and GT for “greater than”.

compare :: Ord a => a -> a -> Ordering
(<) :: Ord a => a -> a -> Bool
(>) :: Ord a => a -> a -> Bool
(>=) :: Ord a => a -> a -> Bool
(<=) :: Ord a => a -> a -> Bool
max :: Ord a => a -> a -> a
min :: Ord a => a -> a -> a

Prelude> compare 1 1                -- 1 is EQual to 1
EQ
Prelude> compare 1 3                -- 1 is Less Than 3
LT
Prelude> compare 1 0                -- 1 is Greater Than 0
GT
Prelude> min 5 3
3
Prelude> max 5 3
5
Prelude> "aardvark" < "banana"      -- strings are compared alphabetically
True
Prelude> [1,2,3] > [2,5]            -- lists are compared like strings
False
Prelude> [1,2,3] > [1,1]
True

When we can compare values, we can also sort lists of them. The function sort from Data.List works on all types that belong to the Ord class.

Prelude> import Data.List
Prelude Data.List> :t sort
sort :: Ord a => [a] -> [a]
Prelude Data.List> sort [6,1,4,8,2]
[1,2,4,6,8]
Prelude Data.List> sort "black sphinx of quartz, judge my vow!"     -- remember, strings are lists!
"      !,aabcdefghijklmnoopqrstuuvwxyz"

As a last example, let’s sort a list of lists according to length. We’ll need two helper functions:

-- from the module Data.Ord
-- compares two values "through" the function f
comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing f x y = compare (f x) (f y)

-- from the module Data.List
-- sorts a list using the given comparison function
sortBy :: (a -> a -> Ordering) -> [a] -> [a]

Now the implementation of sortByLength is straightforward:

-- sorts lists by their length
sortByLength :: [[a]] -> [[a]]
sortByLength = sortBy (comparing length)

sortByLength [[1,2,3],[4,5],[4,5,6,7]]   ==>  [[4,5],[1,2,3],[4,5,6,7]]

Num, Integral, Fractional, Floating

The Num class contains integer arithmetic:

(+) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> a
(*) :: Num a => a -> a -> a
negate :: Num a => a -> a    -- 0-x
abs :: Num a => a -> a       -- absolute value
signum :: Num a => a -> a    -- -1 for negative values, 0 for 0, +1 for positive values
fromInteger :: Num a => Integer -> a

Num also shows up in the types of integer literals:

Prelude> :t 12
12 :: Num p => p

This means that a literal like 12 can be interpreted as a member of any type implementing Num. When GHC reads a number literal like, 12 it produces code that corresponds to fromIntegral 12.

Prelude> 1 :: Int
1
Prelude> 1 :: Double
1.0
Prelude> fromIntegral 1 :: Double
1.0

Integral is the class of types that represent whole numbers, like Int and Integer. The most interesting functions are div and mod for integer division and remainder. All types that belong to Integral also belong to Num.

div :: Integral a => a -> a -> a
mod :: Integral a => a -> a -> a

Fractional is the class for types that have division. All types that belong to Fractional also belong to Num.

(/) :: Fractional a => a -> a -> a

Floating contains some additional operations that only make sense for floating point numbers. All types that belong to Floating also belong to Fractional (and to Num).

sqrt :: Floating a => a -> a
sin :: Floating a => a -> a

Read and Show

The Show and Read classes are for the functions show and read, that convert values to and from Strings.

show :: Show a => a -> String
read :: Read a => String -> a

Prelude> show 3
"3"
Prelude> read "3" :: Int
3
Prelude> read "3" :: Double
3.0

As you can see above, you often need to use a type annotation with read so that the compiler can choose the right implementation.

Sidenote: Foldable

One more thing! You might remember that it was mentioned earlier that the type of length isn’t [a] -> Int but something more general. Let’s have a look:

Prelude> :t length
length :: Foldable t => t a -> Int

This type looks a bit different than the ones we’ve seen before. The type variable t has an argument a. We’ll look at type classes like this in more detail in part 2, but here’s a crash course.

What Foldable represents is types that you can fold over. The true type of foldr is:

foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

We’ve succesfully used the fact that lists are Foldable since we’ve managed to use length and foldr on lists. However, Maybe is also Foldable! The Foldable instance for Maybe just pretends that values of Maybe a are like lists of length 0 or 1:

foldr (+) 1 Nothing   ==> 1
foldr (+) 1 (Just 3)  ==> 4
length Nothing        ==> 0
length (Just 'a')     ==> 1

We’ll meet some more foldable types next.

Exercises

All exercises can be found in Set4a and Set4b. Please pay attention in the title of the exercise in which file the exercises of this section can be found.

Exercises from 4a:

You have reached the end of this section! Continue to the next section:

You can check your current points from the blue blob in the bottom-right corner of the page.