# Lecture 3

Typeclasses

## Common properties

maxInt :: Int -> Int -> Int
maxInt x y = if x > y then x else y
maxChar :: Char -> Char -> Char
maxChar x y = if x > y then x else y

Why not just? 🤔

max :: a -> a -> a
max x y = if x > y then x else y

👩‍🔬 The above type signature of max implies that the function can work with any type. But we don't know anything about the type!

Compare:

take :: Int -> [a] -> [a]

## Polymorphisms

📜 Parametric polymorphism — same behavior for different types.

📜 Ad-hoc polymorphism — different behavior for different types.

Examples

• The first element of a pair
• Reversing a list
• List length
• Taking 5 elements from a list
• Function composition
• Equality
• Comparison
• Conversion to string
• Parsing from a string

🚂 Parametric

## class

  ┌── "class" keyword
│
│      ┌── Typeclass name
│      │
│      │    ┌── Type variable
│      │    │
│      │    │   ┌── "where" keyword
class Display a where

display :: a -> String
│          │
│          └─ method type signature
│
method name

## instance

class Display a where
display :: a -> String
instance Display Bool where
display False = "false"
display True  = "true"
greet :: Display a => a -> String
greet val = "Hello, " ++ display val
displayBoth :: (Display a, Display b) => a -> b -> String
displayBoth a b = display a ++ " and " ++ display b
instance Display Char where
display c = [c]
ghci> :t greet
greet :: Display a => a -> String
ghci> :t display
display :: Display a => a -> String

ghci> greet 'A'
"Hello, A"
ghci> displayBoth 'x' True
"x and true"

## Separation of concerns

data

class

instance

What is stored inside?

What we can do with this?

How we implement this behavior for that data?

## Default methods

class Display a where
{-# MINIMAL display #-}

display :: a -> String

displayList :: [a] -> String
displayList l =
"[" ++ intercalate ", " (map display l) ++ "]"

🐎 More performant methods

🦎 Different behaviour

🐘 Big typeclasses

🐰 Small typeclasses

🐁 Smaller possibility of error

🦁 Easier to write instances

## Not Java interfaces

displayList :: Display a => [a] -> String

🦸‍♀️ Typeclasses take power, not grant.

ghci> displayList [True, False, True]
"[true, false, true]"

ghci> displayList "Hello!"
"[H, e, l, l, o, !]"
ghci> displayList [True, 'X']

<interactive>:31:20: error:
• Couldn't match expected type ‘Bool’ with actual type ‘Char’
• In the expression: 'X'

🚫 displayList takes a list of values that can be converted to String.

👍 displayList takes a list of values of the same type and this type can be converted to String.

## {-# LANGUAGE #-}

{-# LANGUAGE InstanceSigs #-}

module Display where

class Display a where
display :: a -> String

instance Display Char where
display :: Char -> String  -- 👈 needed for this
display c = [c]

ℹ️ GHC has features not enabled by default. Use {-# LANGUAGE #-} pragma at the top of your file to enable them.

## Standard Typeclasses

Eq — check for equality

Ord — compare

Show — convert to String

Bounded — has minimal and maximal value

Enum — is an enumeration

Num — a number (addition, multiplication, subtraction, etc.)

ghci> :info Bounded
type Bounded :: * -> Constraint
class Bounded a where
minBound :: a
maxBound :: a
{-# MINIMAL minBound, maxBound #-}
-- Defined in ‘GHC.Enum’
instance Bounded Word -- Defined in ‘GHC.Enum’
instance Bounded Int -- Defined in ‘GHC.Enum’
...

## Eq

class Eq a where
{-# MINIMAL (==) | (/=) #-}

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

x /= y = not (x == y)
x == y = not (x /= y)
ghci> :t (==)
(==) :: Eq a => a -> a -> Bool

ghci> 'x' == 'F'
False

ghci> [1..5] /= reverse [5, 4, 3, 2, 1]
False

ghci> "" == []
True

## Ord

class (Eq a) => Ord a where
{-# MINIMAL compare | (<=) #-}

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

compare x y = if x == y then EQ
else if x <= y then LT
else GT

...
data Ordering
= LT  -- ^ Less
| EQ  -- ^ Equal
| GT  -- ^ Greater
sort   :: Ord a => [a] -> [a]
sortBy :: (a -> a -> Ordering) -> [a] -> [a]

## Num

class Num a where
(+), (-), (*)       :: a -> a -> a
negate              :: a -> a
abs                 :: a -> a
signum              :: a -> a

fromInteger         :: Integer -> a

x - y               = x + negate y
negate x            = 0 - x
ghci> :t 42
42 :: Num p => p
ghci> 42
42

🍬 Syntax sugar everywhere

ghci> fromInteger (42 :: Integer)
42

## Type inference

ghci> check x y = x + y < x * y
ghci> :t check
check :: (Ord a, Num a) => a -> a -> Bool

## deriving

data Color
= Red
| Green
| Blue
deriving (Eq, Ord, Show, Read, Enum, Bounded, Ix)

⚗️ GHC can automatically generate instances for you*

data Bit
= Zero
| One
deriving (Num)
<interactive>:5:15: error:
• Can't make a derived instance of ‘Num Bit’:
‘Num’ is not a stock derivable class (Eq, Show, etc.)
Try enabling DeriveAnyClass
• In the data declaration for ‘Bit’

## Generaliz(s)edNewtypeDeriving

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Size = Size
{ unSize :: Int
} deriving ( Show
, Eq
, Ord
, Enum
, Bounded
, Ix
, Num
, Integral
, Real
, Bits
, FiniteBits
)

⚗️ For newtype, you can derive any typeclass of the original type

## Algebra

class Semigroup a where
(<>) :: a -> a -> a

⚗️ Typeclass for smashing things together

Equivalent typeclass

class Appendable a where
append :: a -> a -> a
instance Semigroup [a] where
(<>) = (++)

ℹ️ Standard instances

👩‍🔬 But with laws! Associativity:

a <> (b <> c) \equiv (a <> b) <> c
instance Semigroup Bool where
(<>) = ??? -- && or || , which one to choose ????

## newtype again

newtype Any = Any { getAny :: Bool }
newtype All = All { getAll :: Bool }

⚗️ newtypes help implement different behaviour for the same type

instance Semigroup Any where
Any x <> Any y = Any (x || y)
instance Semigroup All where
All x <> All y = All (x && y)
ghci> Any False <> Any True
Any {getAny = True}
ghci> All False <> All True
All {getAll = False}

ghci> Any False <> All True

<interactive>:4:14: error:
• Couldn't match expected type ‘Any’ with actual type ‘All’
• In the second argument of ‘(<>)’, namely ‘All True’

## Everything is Semigroup!

newtype Any =
Any { getAny :: Bool }
newtype All =
All { getAll :: Bool }
newtype Sum a =
Sum { getSum :: a }
newtype Product a =
Product { getProduct :: a }
newtype First a =
First { getFirst :: a }
newtype Last a =
Last { getLast :: a }
Ordering
Maybe a
[a]
(a, b)
...

Booleans with ||

Booleans with &&

Numbers with +

Numbers with *

Anything with taking first

Anything with taking last

## Okay, not really everything...

newtype Sub a = Sub { getSub :: a }

instance Num a => Semigroup (Sub a) where
Sub x <> Sub y = Sub (x - y)

🔢 Numbers with subtraction (-)

1 - (2 - 3) \neq (1 - 2) - 3

🚫 Associativity doesn't hold!

## Algebra, part 2

class Semigroup a => Monoid a where
mempty :: a

⚗️ Smashing with a neutral element

instance Monoid [a] where
mempty = []

Standard instances

👩‍🔬 Laws again!

x <> \mathrm{mempty} \equiv x
instance Monoid Any where
mempty = Any False

Right identity

\mathrm{mempty} <> x \equiv x

Left identity

instance Monoid All where
mempty = All True
instance Num a => Monoid (Sum a) where
mempty = Sum 0
instance Num a => Monoid (Product a) where
mempty = Product 1

## Not everything is Monoid!

newtype First a = First { getFirst :: a }

⚗️ Not every data type has a neutral element for <>

instance Semigroup (First a) where
a <> _ = a
instance Monoid (First a) where
mempty = ???

We need: mempty <> x ≡ x

## Modules

base has two First data types

newtype First a =
First { getFirst :: a }
newtype First a =
First { getFirst :: Maybe a }

Data.Semigroup

Data.Monoid

# Kind

## Be kind to us, Haskell 🙏

📜 Kind — a type of a type.

ghci> :k Int
Int :: *

ghci> :k String
String :: *
ghci> :k Maybe
Maybe :: * -> *

ghci> :k []
[] :: * -> *

ghci> :k Either
Either :: * -> * -> *

ghci> :k (->)
(->) :: * -> * -> *

📜 Types like Maybe, Either, etc. are called type constructors.

👩‍🔬 The following type signature doesn't compile because the function arrow expects two types of kind * but Maybe is * -> *

maybeToList :: Maybe -> [a]
interactive>:8:16: error:
• Expecting one more argument to ‘Maybe’
Expected a type, but ‘Maybe’ has kind ‘* -> *’


👩‍🔬 In some sense, type constructors are like functions: they take arguments (other types) to become complete types. They can also be partially applied!

ghci> :k Either
Either :: * -> * -> *

ghci> :k Either Int
Either Int :: * -> *

ghci> :k Either Int String
Either Int String :: *

## Why?

👩‍🔬 Haskell allows polymorphism over type constructors

## Typeclass for type constructors

ℹ️ A typeclass for creating singleton "containers" from values

class Singleton f where
singleton :: a -> f a

From the above typeclass definition we deduce the following facts:

1. It's for type constructors (Maybe, [], etc.) and not e.g. Int
2. Value types inside the type constructor should be the same.
instance Singleton Maybe where
singleton :: a -> Maybe a
singleton x = Just x
instance Singleton [] where
singleton :: a -> [a]
singleton x = [x]
ghci> singleton 3 :: Maybe Int
Just 3

ghci> singleton 3 :: [Int]

ghci> :t singleton
singleton :: Singleton f => a -> f a

## Functor

ℹ️ Mapping values inside context f

class Functor f where
fmap :: (a -> b) -> f a -> f b
instance Functor Maybe      where ...
instance Functor []         where ...
instance Functor (Either e) where ...

ℹ️ fmap is a generalization of map

ghci> :t map
map :: (a -> b) -> [a] -> [b]

ghci> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
ghci> fmap (+ 5) (Just 7)
Just 12
ghci> fmap not [True, False, True]
[False,True,False]
ghci> fmap (drop 8) (Right [0 .. 10])
Right [8,9,10]
ghci> fmap (drop 8) (Left "Hello, Haskell!")
Left "Hello, Haskell!"

## Functor laws

ℹ️ Identity function

💎 Correct Maybe instance

id :: a -> a
id x = x
instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap _ Nothing  = Nothing
fmap f (Just x) = Just (f x)

Functor law 1: Identity

Functor law 2: Composition

\mathrm{fmap} \ \mathrm{id} \equiv \mathrm{id}
\mathrm{fmap} \ (f\ .\ g) \equiv \mathrm{fmap} \ f \ . \ \mathrm{fmap} \ g

🐞 Incorrect Maybe instance

instance Functor Maybe where
fmap :: (a -> b) -> Maybe a -> Maybe b
fmap f m = Nothing

## Folds

Folds for lists

         "step" function
│
┌────┼────┐
foldr :: (a -> b -> b) -> b -> [a] -> b
│     │     │
│     │     └─ final result
initial value  ───┘     │
│
list of values
ghci> foldr (+) 0 [1 .. 5]
15

ghci> foldr (*) 3 [2, 4]
24

## Everything is a Fold!

Folds can be used to express almost everything you need!

• Sum of elements

• Length

• Pair of length and sum (to get average)

• The first element (aka head)

• Index of the first zero

• List of all elements

• List of every second element

• ...

👩‍🔬 However, this doesn't mean that you always must use a fold. Sometimes, an explicit recursive function is easier to read.

## How foldr folds

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ z [] = z
foldr f z (x : xs) = f x (foldr f z xs)
foldr f z [1,2,3] == f 1 (f 2 (f 3 z)) 
foldr (+) 0 [1, 2, 3]
1  :  2  :  3  : []
1  + (2  + (3  + 0))

👩‍🔬 foldr f z replaces every list constructor (:) with f and [] with z

👩‍🔬 Use foldr when the function is lazy on the second argument

ghci> foldr (&&) True (repeat False)
False

ghci> foldr (\x _ -> Just x) Nothing [1 .. ]
Just 1

## How foldl/foldl' folds

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl _ z [] = z
foldl f z (x : xs) = foldl f (f z x) xs
foldl f z [1,2,3] == f (f (f z 1) 2) 3
foldl (+) 0 [1, 2, 3]
1  :  2   :  3  : []
((0  +  1) +  2)  +  3

⚠️ foldl always leaks memory due to its nature

👩‍🔬 foldl' is a strict version of foldl

ghci> foldl' (\acc x -> x + acc) 0 [1 .. 10]
55

ghci> foldl' (\acc _ -> acc + 1) 0 [1 .. 10]
10

## foldr vs foldl

foldr  :: (a -> b -> b) -> b -> [a] -> b
foldl' :: (b -> a -> b) -> b -> [a] -> b


👩‍🔬 Haskell has many folds. Choose between foldr / foldl'

ghci> foldr (-) 0 [1..5]
3
0 - (1 - (2 - (3 - (4 - 5))))
ghci> foldl' (-) 0 [1..5]
-15
((((0 - 1) - 2) - 3) - 4) - 5

## Foldable

ℹ️ Folding any structure t that contains elements

class Foldable t where
foldr :: (a -> b -> b) -> b -> t a -> b
    foldMap :: Monoid m => (a -> m) -> t a -> m

... and 15 more methods ...
ghci> :t foldr
foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

ghci> :t sum
sum :: (Foldable t, Num a) => t a -> a

ghci> :t concat
concat :: Foldable t => t [a] -> [a]

🙌 Now the standard Haskell library makes sense!

ghci> foldr (-) 10 (Just 3)
-7

## Strict evaluation

🦥 Laziness can lead to space leaks

sum :: [Int] -> Int
sum = go 0
where
go :: Int -> [Int] -> Int
go acc []       = acc
go acc (x : xs) = go (acc + x) xs
sum [3, 1, 2]
= go 0 [3, 1, 2]
= go (0 + 3) [1, 2]
= go ((0 + 3) + 1) 
= go (((0 + 3) + 1) + 2) []
= ((0 + 3) + 1) + 2
= (3 + 1) + 2
= 4 + 2
= 6

🐞🔍 Debugging in Haskell: Equational Reasoning

## {-# LANGUAGE BangPatterns #-}

💥 You can force evaluation of lazy computations* with bangs !

{-# LANGUAGE BangPatterns #-}

sum :: [Int] -> Int
sum = go 0
where
go :: Int -> [Int] -> Int
go !acc []       = acc
go !acc (x : xs) = go (acc + x) xs
sum [3, 1, 2]
= go 0 [3, 1, 2]
= go 3 [1, 2]
= go 4 
= go 6 []
= 6

🚰 No more space leaks!

* to some degree