% Intro to Haskell, day 7 % G Bordyugov % Dec 2016
- Monads recap
- The
Maybe
monad - Lists as a monad
- The
State
monad
{-# LANGUAGE InstanceSigs #-}
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
For example, if m = Maybe
:
return :: a -> Maybe a
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
(>>) :: Maybe a -> Maybe b -> Maybe b
Context: computations that can optionally fail
instance Monad Maybe where
return :: a -> Maybe a
return x = Just x
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing
(Just x) >>= f = f x
(>>) :: Maybe a -> Maybe b -> Maybe b
Nothing >> x = Nothing
x >> Nothing = Nothing
(Just x) >> (Just y) = Just y
Composing math functions:
sqrt :: Double -> Maybe Double
arcsin :: Double -> Maybe Double
arccos :: Double -> Maybe Double
without monads:
asa x = case arcsin x of
Nothing -> Nothing -- shortcut
Just x -> case sqrt x of
Nothing -> Nothing -- shortcut
Just y -> arccos y
Composing math functions:
sqrt :: Double -> Maybe Double
arcsin :: Double -> Maybe Double
arccos :: Double -> Maybe Double
with monads:
asa x =
arcsin x >>= sqrt >>= arccos
Looking ups
phonebook :: [(String, String)]
phonebook = [ ("Bob", "01788 665242"),
("Fred", "01624 556442"),
("Alice", "01889 985333"),
("Jane", "01732 187565") ]
lookup :: Eq a => a -- a key
-> [(a, b)] -- the lookup table to use
-> Maybe b -- the result of the lookup
Prelude> lookup "Bob" phonebook
Just "01788 665242"
Prelude> lookup "Zoe" phonebook
Nothing
cars :: [(String, String)]
cars = [ ("01788 665242", "P 101 BA"),
("01732 187565", "B 4242 D") ]
getCarPlate :: String
-> Maybe String
getCarPlate name =
lookup name phonebook >>=
(\number -> lookup number cars)
getCarPlate :: String
-> Maybe String
getCarPlate name =
lookup name phonebook >>=
(\number -> lookup number cars)
is equivalent to
getCarPlate name = do
number <- lookup name phonebook
return (lookup number cars)
asa a = arcsin a >>= sqrt >>= arccos
can be written as
asa a =
arcsin a >>= (\b ->
sqrt b >>= (\c ->
arccos c >>= (\d -> return d)))
and hence
asa a = do
b <- arcsin a
c <- sqrt b
d <- arccos c
return d
Context: non-deterministic computations, i.e. returning multiple outcomes
instance Monad [] where
return :: a -> [a]
return x = [x]
(>>=) :: [a] -> (a -> [b]) -> [b]
as >>= f = [b | a <- as, b <- f a]
(>>) :: [a] -> [b] -> [b]
as >> bs = bs
Our ``F*cebook''-application with
getFriends :: User -> [Users]
Getting friends of friends:
getFriendsOfFriends :: User -> [User]
getFriendsOfFriends u = do
f <- getFriends u
ff <- getFriends f
return ffriend
Equivalent to
getFriendsOfFriends u =
[ff | f <- getFriends u, ff <- getFriends f]
Context: stateful computations
newtype State s a =
State { runState :: s -> (a, s) }
As mentioned a couple of weeks ago, this automatically introduces the accessor
runState :: State s a -> (s -> (a, s))
Testfrage: what about functions accepting more than state, i.e.
f :: b -> s -> (a, s)
?
type StackState = State [Int]
pop :: StackState Int
pop = State $ \s -> (head s, tail s)
push :: Int -> StackState ()
push x = State $ \s -> ((), x:s)
isEmpty :: StackState Bool
isEmpty = State $ \s -> (null s, s)
Using our stack:
runState isEmpty [] -- => (True, [])
runState (push 3) [] -- => ((), [3])
runState (push 5) [3] -- => ((), [5, 3])
runState isEmpty [5, 3] -- => (False, [5, 3])
runState pop [5, 3] -- => (5, [3])
Have to thread the state along the chain manually - doof!
Would be cool if we could write
doStuffWithStack = do
push 3
push 5
x <- pop
return x
runState doStuffWithStack []
-- => (5, [3])
runState doStuffWithStack [3, 2, 1]
-- => (5, [3, 3, 2, 1])
Need a monad instance for State
!
instance Functor (State s) where
fmap f (State a) = State $ \s ->
let (x, s') = a s in (f x, s')
instance Applicative (State s) where
pure x = State $ \s -> (x, s)
a <*> b = State $ \s ->
let (f, s' ) = runState a s
(x, s'') = runState b s'
in (f x, s'')
instance Monad (State s) where
return :: a -> State s a
return x = State $ \s -> (x, s)
(>>=) :: State s a -> (a -> State s b)
-> State s b
a >>= f = State $
\s -> let (x, s') = runState a s
in runState (f x) s'
Just two primitive functions:
put :: s -> State s ()
put s = State $ \_ -> ((), s)
get :: State s s
get = State $ \s -> (s, s)
pop = do
s <- get
put $ tail s
return $ head s
push x = do
y <- get
put x:y
isEmpty = do
s <- get
if null s then return True
else return False
Kind of
type IO = State World
where World
reflects the state of the world, including the
periphery, input-output, memory, other processes, etc.