Programming Languages

Higher Order Constructs in Haskell

Jim Posen - ECE/CS 2014

Defining Typeclasses

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

Typeclass Inheritance

class (Eq a) => Num a where
    ...

Implementing Typeclasses

data TrafficLight = Red | Yellow | Green

instance Eq TrafficLight where
    Red == Red = True
    Green == Green = True
    Yellow == Yellow = True
    _ == _ = False

What if you have a type constructor instead of a type?

instance (Eq m) => Eq (Maybe m) where
    Just x == Just y = x == y
    Nothing == Nothing = True
    _ == _ = False

Practice

Let’s practice interpreting function types

a -> b
(a -> b) -> [a] -> [b]
(b -> c) -> (a -> b) -> a -> c

Functors

class Functor f where
    fmap :: (a -> b) -> f a -> f b

Lists are functors

instance Functor [] where
    fmap = map

Why is the declaration not this?

instance Functor [a] where
    fmap = map

f is a type constructor

The Maybe Functor

Can you write fmap for Maybe?

instance Functor Maybe where
    fmap f (Just x) = Just (f x)
    fmap f Nothing = Nothing

Trees

How about Functor Tree ?

instance Functor Tree where
    fmap f EmptyTree = EmptyTree
    fmap f (Node v left right) = Node (f v) (fmap f left) (fmap f right)

IO

instance Functor IO where
    fmap f action = do
        result <- action
        return (f result)

Function Type Constructors

What is the type constructor (->) r ?

Well ((->) r) a is equivalent to (->) r a is equivalent to r -> a

It’s just a type constructor creating a function type

Functions as Functors

How do we implement fmap over function type constructors?

instance Functor ((->) r) where
    fmap = ...

What is the type of fmap here?

(a -> b) -> (r -> a) -> (r -> b)

That’s our type for function composition!

instance Functor ((->) r) where
    fmap = (.)

Functor Laws

  • There are two laws of Functors that cannot be enforced by the Haskell compiler

First Law

fmap id = id

Second Functor Law

fmap (f . g) = fmap f . fmap g

Functors

  • So what is fmap ?
  • It is a generalized map
  • So what is a Functor?
  • A type constructor where the “contents” can be transformed

Functors are not enough

  • fmap lets us operate on on Functor
  • What if we want to operate on multiple functors?
  • For example, how would you add two Maybe Ints?
  • Clearly we need a new structure

Sneak Peek

It turns out we can do something like this

ghci> (+) <$> Just 4 <*> Just 5
Just 9
ghci> (+) <$> Nothing <*> Just 5
Nothing

But we need some machinery first

Applicative Functors

  • pure “wraps” the argument in the functor
  • <*> is like fmap but the mapping function is inside the functor
class (Functor f) => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

Applicative Maybe

instance Applicative Maybe where
    pure = Just
    Nothing <*> _ = Nothing
    (Just f) <*> something = fmap f something
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Nothing
Nothing
ghci> Nothing <*> Just 5
Nothing
ghci> pure (+) <*> Just 5 <*> Just 7
Just 12

Small shortcut

  • What is pure f <*> arg doing?
  • It is just fmap f arg
  • <$> is a sugary infix fmap operator

Applicative []

instance Applicative [] where
    pure x = [x]
    fs <*> xs = [f x | f <- fs, x <- xs]
ghci> [(+), (*)] <*> [1, 2] <*> [3, 4]
[4,5,5,6,3,4,6,8]

Applicative IO

instance Applicative IO where
    pure = return
    a <*> b = do
        f <- a
        x <- b
        return (f x)
ghci> (++) <*> getLine <*> getLine
Hello
 World!
"Hello World!"

Monads

What is a Monad?

A monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.

Let’s start somewhere else.

Side Effects

  • Imperative languages are expressed as a sequence of state-changing operations
  • Think about assembly code
  • The only machine instruction that does not change state is a no-op
  • Functional languages are based on expressions that are evaluated
  • There are no sequential instructions and there are no side effects
  • But we really need side effects
  • And we really want sequential instructions
  • The closest thing we have to sequential instructions is the ability to pass a value through a chain of functions
  • That will have to be good enough
  • We want to create a pipeline of functions that operate on wrapped values

Monads

class (Applicative m) => Monad m where
    return :: a -> m a

    (>>=) :: m a -> (a -> m b) -> m b

    (>>) :: m a -> m b -> m b
    x >> y = x >>= \_ -> y

return

  • return is exactly like pure
  • Different for historical reasons
  • Just wraps its argument in a monad
  • Not like return in other languages
  • We saw it for IO (hint: IO is a monad)

>>=

  • How we will apply a regular function to a monad

Monad Maybe

instance Monad Maybe where
    return x = Just x
    Nothing >>= f = Nothing
    Just x >>= f  = f x

How this is used

  • The Maybe monad is very useful
  • Imagine a sequence of operations, any of which may fail
  • Each function takes an a and returns a Maybe a because it can fail
  • You still want to chain your sequence of operations together and have Nothing at the end if any step failed

A truncated example

See Learn You a Haskell for the full example

return (0,0) >>= landLeft 1 >> landRight 4 >>= landLeft 2 >>= landLeft 1
routine = case landLeft 1 (0,0) of
    Nothing -> Nothing
    Just pole1 -> case landRight 4 pole1 of
        Nothing -> Nothing
        Just pole2 -> case landLeft 2 pole2 of
            Nothing -> Nothing
            Just pole3 -> landLeft 1 pole3

IO Revisited

Remember our greet program?

greet :: IO ()
greet = do
    putStr "What is your first name? "
    firstName <- getLine
    putStr "What is your last name? "
    lastName <- getLine
    putStrLn $ "Hi " ++ firstName ++ " " ++ lastName ++ "!"
    return ()

Greet with the IO monad

greet :: IO ()
greet =
    putStr "What is your first name? " >>
    getLine >>= (\firstName ->
        putStr "What is your last name? " >>
        getLine >>= (\lastName ->
            putStrLn ("Hi " ++ firstName ++ " " ++ lastName ++ "!") >>
            return ()
        )
    )

Mind Blowing Slide 3

  • do is just syntactic sugar for monad operations
  • do blocks allow you to specify a sequence of operations
  • Under the covers, they are translated to monad operations
  • Monads allow us to contain side effects from sequential instructions

do blocks with Maybe

routine = case landLeft 1 (0,0) of
    Nothing -> Nothing
    Just pole1 -> case landRight 4 pole1 of
        Nothing -> Nothing
        Just pole2 -> case landLeft 2 pole2 of
            Nothing -> Nothing
            Just pole3 -> landLeft 1 pole3
routine :: Maybe (Int, Int)
routine = do
     start <- return (0,0)
     pole1 <- landLeft 1 start
     pole2 <- landRight 4 pole1
     pole3 <- landLeft 2 start
     return $ landLeft 1 pole3

Monad []

instance Monad [] where
    return x = [x]
    xs >>= f = concat (map f xs)

Using Monad []

ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

In do notation

listOfTuples :: [(Int,Char)]
listOfTuples = do
    n <- [1,2]
    ch <- ['a','b']
    return (n,ch)

Mind Blowing Slide 4

  • List compehensions are just syntactic sugar for the list monad
[(n,ch) | n <- [1,2], ch <- ['a','b']]