# Functors, Applicatives, and Monads --- CS 135 // 2021-03-16 # Questions ## ...about anything? # Functors ## Map, Revisited - Commonly, we want to apply a function to each element in a list ```haskell double :: [Int] -> [Int] double [] = [] double (x:xs) = 2 * x : double xs ``` - This is called a **map** can be generalized with a higher-order function: ```haskell map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = f x : map f xs ``` ## Generalizing Map - Using `map`, we can write functions much easier: ```haskell double :: [Int] -> [Int] double = map (*2) square :: [Int] -> [Int] square = map (^2) ``` - In general, this idea of mapping a function over a data structure isn't unique to lists - We might want to be able to do things like: ```haskell treeMap :: (a -> b) -> Tree a -> Tree b ``` ```haskell maybeMap :: (a -> b) -> Maybe a -> Maybe b ``` ## Functor Typeclass - The `Functor` typeclass exactly captures this idea of map and is defined as ```haskell class Functor f where fmap :: (a -> b) -> f a -> f b ``` - Note that `f` must be a **parameterized** type such as `Maybe` or `Tree` or a list ## Instances of Functor - We can create a `Functor` instance for lists with: ```haskell instance Functor [] where fmap = map ``` - What would an instance for `Maybe` look like? ```haskell instance Functor Maybe where -- fmap :: (a -> b) -> Maybe a -> Maybe b fmap _ Nothing = Nothing fmap f (Just x) = Just (f x) ``` - Now we can do things like: ```text Prelude> fmap (*2) Nothing Nothing Prelude> fmap (*2) (Just 7) Just 14 ``` ## Tree Functor - In the previous lab, we defined the type: ```haskell data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Eq) ``` - Let's define an instance of `Functor` for it: ```haskell instance Functor Tree where -- fmap :: (a -> b) -> Tree a -> Tree b fmap _ Empty = Empty fmap f (Node x l r) = Node (f x) l' r' where l' = fmap f l r' = fmap f r ``` # Applicatives ## Generalizing Map - A `Functor` captures the idea of mapping a unary function over a parameterized type - But what about functions with more than one parameter? - It is temping to want to define more `fmap` functions such as: ```haskell fmap2 :: (a -> b -> c) -> f a -> f b -> f c ``` - This would allow us to do thing like: ```haskell fmap2 (+) (Just 3) (Just 5) ``` ## Applicative Typeclass - The `Applicative` typeclass captures the idea of mapping functions with arbitrarily many parameters over data structures ```haskell class Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b ``` - How might we use this? ```text Prelude> pure (+) <*> (Just 3) <*> (Just 5) Just 5 ``` ## Applicative Maybe - How might we implement this for `Maybe`? ```haskell instance Applicative Maybe where -- pure :: a -> Maybe a pure = Just -- (<*>) :: Maybe (a -> b) -> Maybe a -- -> Maybe b Nothing <*> _ = Nothing _ <*> Nothing = Nothing (Just f) <*> (Just x) = Just (f x) ``` ## Applicative List - How might we implement this for `[]`? ```haskell instance Applicative [] where -- pure :: a -> [a] pure x = [x] -- (<*>) :: [a -> b] -> [a] -> [b] fs <*> xs = [f x | f <- fs, x <- xs] ``` ## Using Applicative List - What will the following evaluate to? ``` pure (+) <*> [1, 2, 3] <*> [100, 200, 300] ``` ``` ==> [(+)] <*> [1, 2, 3] <*> [100, 200, 300] ``` ``` ==> [(1+), (2+), (3+)] <*> [100, 200, 300] ``` ``` ==> [101, 201, 301, 102, 202, 302, 103, 203, 303] ``` # Monads ## Mutable State with Immutable Data - We have primarily only worked with pure functions with no mutable datatypes or variables - To encode a computation with mutating state, functional programming resort to the **monad** ## Monad Typeclass - The `Monad` typeclass looks almost identical to `Applicative` but is slightly more general ```haskell class Monad f where return :: a -> f a (>>=) :: f a -> (a -> f b) -> f b ``` - `return` is just another name for `pure` and is included for historical reasons - `>>=` is similar to `<*>` except `f (a -> b)` is replaced with the more general `a -> f b` ## Monad Instance for Maybe - How might we implement this for `Maybe`? ```haskell instance Monad Maybe where -- return :: a -> Maybe a return = Just -- (>>=) :: Maybe a -> (a -> Maybe b) -- -> Maybe b Nothing >>= _ = Nothing (Just x) >>= f = f x ``` ## Application of Maybe Monad - Now suppose we have three operations: ```haskell f1 :: a -> Maybe b f2 :: b -> Maybe c f3 :: c -> Maybe d ``` - How could we compose them into a function? ```haskell f4 :: a -> Maybe d ``` ## Without a Monad - If `Maybe` wasn't a monad, our only choice is: ```haskell f4 x = case f1 x of Nothing -> Nothing Just y -> case f2 y of Nothing -> Nothing Just z -> f3 z ``` ## With a Monad - However, since `Maybe` is a monad, we can compose these operations in the following way: ```haskell f4 x = f1 x >>= \y -> f2 y >>= \z -> f3 z ``` - Composing monadic operations like this is so common, Haskell has special syntax for it: ```haskell f4 x = do y <- f1 x -- binds the result to y z <- f2 y -- binds the result to z f3 z -- the result of f3 is returned ``` ## Monadic List - How might we implement `Monad` for `[]`? ```haskell instance Monad [] where -- return :: a -> [a] return x = [x] -- (>>=) :: [a] -> (a -> [b]) -> [b] xs >>= f = [y | x <- xs, y <- f x] ```