2024-11-14 16:13:31 +01:00
---
title:
2024-11-21 16:23:06 +01:00
- Monads in Haskell
2024-11-14 16:13:31 +01:00
author:
- Sanchayan Maity
2024-11-21 16:23:06 +01:00
aspectratio:
- 169
fontsize:
- 14pt
papersize:
- "presentation-16-9"
2024-11-14 16:13:31 +01:00
---
2024-11-21 16:23:06 +01:00
## Agenda
2024-11-14 16:13:31 +01:00
- Recap of Functors
- Recap of Applicative
- Monads
2024-11-21 16:23:06 +01:00
## Functor
2024-11-14 16:13:31 +01:00
```haskell
class Functor f where
fmap :: (a -> b) -> f a -> f b
(< $) :: a -> f b -> f a
```
- Must preserve identity
```haskell
fmap id = id
```
- Must preserve composition of morphism
```haskell
fmap (f . g) == fmap f . fmap g
```
2024-11-21 16:23:06 +01:00
## Higher order kinds
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
- For something to be a functor, it has to be a first order kind[^1].
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
[^1]: [Haskell's Kind System ](https://diogocastro.com/blog/2018/10/17/haskells-kind-system-a-primer/ )
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
## Applicative
2024-11-14 16:13:31 +01:00
```haskell
class Functor f => Applicative (f :: TYPE -> TYPE) where
pure :: a -> f a
(< *>) :: f (a -> b) -> f a -> f b
```
```haskell
(< $>) :: Functor f => (a -> b) -> f a -> f b
(< *>) :: Applicative f => f (a -> b) -> f a -> f b
```
```haskell
fmap f x = pure f < *> x
```
2024-11-21 16:23:06 +01:00
## Examples
2024-11-14 16:13:31 +01:00
```haskell
pure (+1) < *> [1..3]
[2, 3, 4]
[(*2), (*3)] < *> [4, 5]
[8,10,12,15]
("Woo", (+1)) < *> (" Hoo!", 0)
("Woo Hoo!", 1)
(Sum 2, (+1)) < *> (Sum 0, 0)
(Sum {getSum = 2}, 1)
(Product 3, (+9)) < *> (Product 2, 8)
(Product {getProduct = 6}, 17)
(,) < $> [1, 2] < *> [3, 4]
[(1,3),(1,4),(2,3),(2,4)]
```
2024-11-21 16:23:06 +01:00
## Use cases
2024-11-14 16:13:31 +01:00
```haskell
Person
< $> parseString "name" o
< *> parseInt "age" o
< *> parseTelephone "telephone" o
```
2024-11-21 16:23:06 +01:00
Can also be written as[^2]
2024-11-14 16:13:31 +01:00
```haskell
liftA3 Person
(parseString "name" o)
(parseInt "age" o)
(parseTelephone "telephone" o)
```
2024-11-21 16:23:06 +01:00
[^2]: [FP Complete - Crash course to Applicative syntax ](https://www.fpcomplete.com/haskell/tutorial/applicative-syntax/ )
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
## Use cases
2024-11-14 16:13:31 +01:00
```haskell
parsePerson :: Parser Person
parsePerson = do
string "Name: "
name < - takeWhile ( / = ' n ' )
endOfLine
string "Age: "
age < - decimal
endOfLine
pure $ Person name age
```
2024-11-21 16:23:06 +01:00
## Use cases
2024-11-14 16:13:31 +01:00
```haskell
helper :: () -> Text -> () -> () -> Int -> () -> Person
helper () name () () age () = Person name age
parsePerson :: Parser Person
parsePerson = helper
< $> string "Name: "
< *> takeWhile (/= 'n')
< *> endOfLine
< *> string "Age: "
< *> decimal
< *> endOfLine
```
2024-11-21 16:23:06 +01:00
## Lifting
2024-11-14 16:13:31 +01:00
- Seeing Functor as unary lifting and Applicative as n-ary lifting
```haskell
liftA0 :: Applicative f => (a) -> (f a)
liftA1 :: Functor f => (a -> b) -> (f a -> f b)
liftA2 :: Applicative f => (a -> b -> c) -> (f a -> f b -> f c)
liftA3 :: Applicative f => (a -> b -> c -> d) -> (f a -> f b -> f c -> f d)
liftA4 :: Applicative f => ..
```
Where `liftA0 = pure` and `liftA1 = fmap` .
2024-11-21 16:23:06 +01:00
## Monoidal functors
2024-11-14 16:13:31 +01:00
- Remember Monoid?
```haskell
class Monoid m where
mempty :: m
mappend :: m -> m -> m
```
```haskell
($) :: (a -> b) -> a -> b
(< $>) :: (a -> b) -> f a -> f b
(< *>) :: f (a -> b) -> f a -> f b
mappend :: f f f
($) :: (a -> b) -> a -> b
< *> :: f (a -> b) -> f a -> f b
instance Monoid a => Applicative ((,) a) where
pure x = (mempty, x)
(u, f) < *> (v, x) = (u `mappend` v, f x)
```
2024-11-21 16:23:06 +01:00
## Where are monoids again
2024-11-14 16:13:31 +01:00
```haskell
fmap (+1) ("blah", 0)
("blah",1)
("Woo", (+1)) < *> (" Hoo!", 0)
("Woo Hoo!", 1)
(,) < $> [1, 2] < *> [3, 4]
[(1,3),(1,4),(2,3),(2,4)]
liftA2 (,) [1, 2] [3, 4]
[(1,3),(1,4),(2,3),(2,4)]
```
2024-11-21 16:23:06 +01:00
## Function apply
2024-11-14 16:13:31 +01:00
- Applying a function to an `effectful` argument
```haskell
(< $>) :: Functor m => (a -> b) -> m a -> m b
```
2024-11-21 16:23:06 +01:00
## Applicative laws
2024-11-14 16:13:31 +01:00
```haskell
-- Identity
pure id < *> v = v
-- Composition
pure (.) < *> u < *> v < *> w = u < *> (v < *> w)
-- Homomorphism
pure f < *> pure x = pure (f x)
-- Interchange
u < *> pure y = pure ($ y) < *> u
```
2024-11-21 16:23:06 +01:00
## Operators
2024-11-14 16:13:31 +01:00
- `pure` wraps up a pure value into some kind of Applicative
- `liftA2` applies a pure function to the values inside two `Applicative` wrapped values
- `<$>` operator version of `fmap`
- `<*>` apply a wrapped function to a wrapped value
- `*>` , `<*`
2024-11-21 16:23:06 +01:00
- See more at[^3]
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
[^3]: [FP Complete - Crash course to Applicative syntax ](https://www.fpcomplete.com/haskell/tutorial/applicative-syntax/ )
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
## Monad, is that you?
2024-11-14 16:13:31 +01:00
![*_Monads_* ](burrito-monad.png ){width=60%}
2024-11-21 16:23:06 +01:00
- Unreasonable Effectiveness of Metaphor[^4]
[^4]: [The Unreasonable Effectiveness of Metaphor ](https://argumatronic.com/posts/2018-09-02-effective-metaphor.html )
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
## Motivation - I
2024-11-14 16:13:31 +01:00
```haskell
safeInverse :: Float -> Maybe Float
safeInverse 0 = Nothing
safeInverse x = Just (1 / x)
safeSqrt :: Float -> Maybe Float
safeSqrt x = case x < = 0 of
True -> Nothing
False -> Just (sqrt x)
sqrtInverse1 :: Float -> Maybe (Maybe Float)
sqrtInverse1 x = safeInverse < $> (safeSqrt x)
```
2024-11-21 16:23:06 +01:00
## Motivation - I
2024-11-14 16:13:31 +01:00
```haskell
joinMaybe :: Maybe (Maybe a) -> Maybe a
joinMaybe (Just x) = x
joinMaybe Nothing = Nothing
sqrtInverse2 :: Float -> Maybe Float
sqrtInverse2 x = joinMaybe $ safeInverse < $> (safeSqrt x)
-- In general
-- join :: Monad m => m (m a) -> m a
```
2024-11-21 16:23:06 +01:00
## Motivation - II
2024-11-14 16:13:31 +01:00
```haskell
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
x >>= f = case x of
(Just x') -> f x'
Nothing -> Nothing
sqrtInverse :: Float -> Maybe Float
sqrtInverse x = (>>=) (safeSqrt x) safeInverse
-- >>= is also known as `bind`
-- In general
-- (>>=) :: Monad m => m a -> (a -> m b) -> m b
```
2024-11-21 16:23:06 +01:00
## Motivation - III
2024-11-14 16:13:31 +01:00
```haskell
(>=>) :: (a -> Maybe b) -> (b -> Maybe c) -> (a -> Maybe c)
f >=> g = \x -> case f x of
Just x -> g x
Nothing -> Nothing
sqrtInverse3 :: Float -> Maybe Float
sqrtInverse3 = safeSqrt >=> safeInverse
-- In general
-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
```
2024-11-21 16:23:06 +01:00
## Motivations
2024-11-14 16:13:31 +01:00
- Flattening
- Sequencing
- Composition
2024-11-21 16:23:06 +01:00
## Monad
2024-11-14 16:13:31 +01:00
```haskell
class Applicative m => Monad (m :: Type -> Type) where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
import Control.Monad (join)
join :: Monad m => m (m a) -> m a
```
2024-11-21 16:23:06 +01:00
## Just **do**
2024-11-14 16:13:31 +01:00
```haskell
main :: IO ()
main = do
putStrLn "What is your name?"
name < - getLine
let greeting = "Hello, " ++ name
putStrLn greeting
```
2024-11-21 16:23:06 +01:00
## Monad laws
2024-11-14 16:13:31 +01:00
```haskell
-- Left identity
return x >>= f == f x
-- Right identity
x >>= return == x
-- Associativity
m >>= (\x -> k x >>= h) == (m >>= k) >>= h
```
2024-11-21 16:23:06 +01:00
## ???
2024-11-14 16:13:31 +01:00
![*_WTH_* ](endofunctor.jpg ){width=60%}
2024-11-21 16:23:06 +01:00
## Monoids recap
2024-11-14 16:13:31 +01:00
```haskell
class Semigroup m where
(< >) :: m -> m -> m
class Semigroup m => Monoid m where
mempty :: m
-- defining mappend is unnecessary, it copies from Semigroup
mappend :: m -> m -> m
mappend = (< >)
```
2024-11-21 16:23:06 +01:00
## Some Math
2024-11-14 16:13:31 +01:00
![*_Morphism_* ](morphism.png ){width=30%}
- Category: a set of objects and arrows
- Arrows between objects (morphisms): functions mapping one object to another
- Two categories: **Set** and **Hask**
2024-11-21 16:23:06 +01:00
## Categories
2024-11-14 16:13:31 +01:00
- **Set**
- Category of sets
- Every arrow, function from one set to another
- **Hask**
- Similar to **Set**
- Objects are Haskell types like `Int` instead of `Z` or `R`
- Arrows between objects `a` & `b` are functions of type `a -> b`
- `a -> b` also a `Type` in **Hask**
- If `A -> B` and `B -> C` , then `A -> C` ~= `.` in **Hask**
2024-11-21 16:23:06 +01:00
- Fun fact: Function composition forms a monoid! (See [Endo ](https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Monoid.html##t:Endo )).
2024-11-14 16:13:31 +01:00
2024-11-21 16:23:06 +01:00
## Monads are monoids...
2024-11-14 16:13:31 +01:00
In Haskell
- Only work with **Hask** , so functors all map back to **Hask** .
- Functor typeclass are a special type of functor called **endofunctors**
- **endofunctors** map a category back to itself
- Monad is a monoid where
```haskell
-- Operation
>==>
-- Identity
return
-- Set
Type
a -> m b
```
2024-11-21 16:23:06 +01:00
## Now?
2024-11-14 16:13:31 +01:00
![*_WTH_* ](endofunctor2.jpg ){width=80%}
2024-11-21 16:23:06 +01:00
## Contrasts with Monad
2024-11-14 16:13:31 +01:00
- No data dependency between `f a` and `f b`
- Result of `f a` can't possibly influence the behaviour of `f b`
- That needs something like `a -> f b`
2024-11-21 16:23:06 +01:00
## Applicative vs Monads
2024-11-14 16:13:31 +01:00
- Applicative
* Effects
* Batching and aggregation
* Concurrency/Independent
- Parsing context free grammar
2024-11-21 16:23:06 +01:00
- Exploring all branches of computation (see [`Alternative` ](https://hackage.haskell.org/package/base-4.20.0.1/docs/Control-Applicative.html##t:Alternative ))
2024-11-14 16:13:31 +01:00
- Monads
* Effects
* Composition
* Sequence/Dependent
- Parsing context sensitive grammar
- Branching on previous results
2024-11-21 16:23:06 +01:00
## Weaker but better
2024-11-14 16:13:31 +01:00
- Weaker than monads but thus also more common
- Lends itself to optimisation (See Facebook's [Haxl ](https://hackage.haskell.org/package/haxl ) project)
- Always opt for the least powerful mechanism to get things done
- No dependency issues or branching? just use applicative
2024-11-21 16:23:06 +01:00
## State monad
2024-11-14 16:13:31 +01:00
```haskell
newtype State s a = State { runState :: s -> (a, s) }
instance Functor (State s) where
fmap :: (a -> b) -> State s a -> State s b
fmap f (State sa) = State $ \s -> let (a, s) = sa s in (f a, s)
instance Applicative (State s) where
pure :: a -> State s a
pure a = State $ \s -> (a, s)
(< *>) :: State s (a -> b) -> State s a -> State s b
State f < *> State g = State $ \s -> let (aTob, s') = f s in
let (a, s'') = g s' in
(aTob a, s'')
```
2024-11-21 16:23:06 +01:00
## State monad
2024-11-14 16:13:31 +01:00
```haskell
instance Monad (State s) where
return = pure
(>>=) :: State s a
-> (a -> State s b)
-> State s b
(State f) >>= g = State $ \s -> let (a, s') = f s
ms = runState $ g a
in ms s'
(>>) :: State s a
-> State s b
-> State s b
State f >> State g = State $ \s -> let (_, s') = f s
in g s'
get :: State s s
get = State $ \s -> (s, s)
```
2024-11-21 16:23:06 +01:00
## State monad
2024-11-14 16:13:31 +01:00
```haskell
put :: s -> State s ()
put s = State $ \_ -> ((), s)
modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)
eval :: State s a -> s -> a
eval (State sa) x = let (a, _) = sa x
in a
```
2024-11-21 16:23:06 +01:00
## Context
2024-11-14 16:13:31 +01:00
```haskell
type Stack = [Int]
empty :: Stack
empty = []
pop :: State Stack Int
pop = State $ \(x:xs) -> (x, xs)
push :: Int -> State Stack ()
push a = State $ \xs -> ((), a:xs)
tos :: State Stack Int
tos = State $ \(x:xs) -> (x, x:xs)
```
2024-11-21 16:23:06 +01:00
## Context
2024-11-14 16:13:31 +01:00
```haskell
stackManip :: State Stack Int
stackManip = do
push 10
push 20
a < - pop
b < - pop
push (a+b)
tos
testState = eval stackManip empty
```
2024-11-21 16:23:06 +01:00
## Reader monad
2024-11-14 16:13:31 +01:00
```haskell
class Monad m => MonadReader r m | m -> r where
ask :: m r
local :: (r -> r) -> m a -> m a
```
2024-11-21 16:23:06 +01:00
## Context
2024-11-14 16:13:31 +01:00
```haskell
import Control.Monad.Reader
tom :: Reader String String
tom = do
env < - ask
return (env ++ " This is Tom.")
jerry :: Reader String String
jerry = do
env < - ask
return (env ++ " This is Jerry.")
```
2024-11-21 16:23:06 +01:00
## Context
2024-11-14 16:13:31 +01:00
```haskell
tomAndJerry :: Reader String String
tomAndJerry = do
t < - tom
j < - jerry
return (t ++ " " ++ j)
runJerryRun :: String
runJerryRun = runReader tomAndJerry "Who is this?"
```
2024-11-21 16:23:06 +01:00
## Questions
2024-11-14 16:13:31 +01:00
- Reach out on
* Email: me@sanchayanmaity.net
* Mastodon: [sanchayanmaity.com ](https://sanchayanmaity.com/@sanchayan )