Monad

19.1 The Monad Class

A Monad lets the next computation depend on the result of the previous one. That's the key difference from Applicative.

class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b      -- "bind"
  return :: a -> m a                     -- same as pure
  (>>) :: m a -> m b -> m b              -- sequence, discarding the first result

-- Laws:
--   return a >>= f    ==  f a                       (left identity)
--   m >>= return      ==  m                         (right identity)
--   (m >>= f) >>= g   ==  m >>= (\x -> f x >>= g)   (associativity)

19.2 do-Notation

-- These are equivalent:
example1 :: Maybe Int
example1 = safeDiv 100 5 >>= \x -> safeDiv x 2 >>= \y -> return (x + y)

example2 :: Maybe Int
example2 = do
  x <- safeDiv 100 5      -- if Nothing, the whole block is Nothing
  y <- safeDiv x 2
  let z = x + y           -- pure let binding inside do
  return z                -- Just 30

19.3 Monad Instances in Action

-- Maybe: short-circuiting failure
lookupChain :: Maybe String
lookupChain = do
  userId <- lookup "ana" [("ana", 1), ("bo", 2)]
  lookup userId [(1, "ana@example.com")]

-- Either: failure with an error value
parseAge :: String -> Either String Int
parseAge s = do
  n <- maybe (Left ("not a number: " ++ s)) Right (readMaybe s)
  if n < 0 then Left "negative" else Right n

-- List: non-determinism (all possibilities)
pairsSum10 :: [(Int, Int)]
pairsSum10 = do
  x <- [1 .. 9]
  y <- [x .. 9]
  if x + y == 10 then return (x, y) else []
-- [(1,9),(2,8),(3,7),(4,6),(5,5)]

import Control.Monad (guard)
knightMoves :: (Int, Int) -> [(Int, Int)]
knightMoves (c, r) = do
  (dc, dr) <- [(1, 2), (2, 1), (-1, 2), (-2, 1), (1, -2), (2, -1), (-1, -2), (-2, -1)]
  let (c', r') = (c + dc, r + dr)
  guard (c' `elem` [1 .. 8] && r' `elem` [1 .. 8])
  return (c', r')

-- Functions ((->) r): the Reader monad (Module 25)
addStuff :: Int -> Int
addStuff = do
  a <- (* 2)
  b <- (+ 10)
  return (a + b)
-- addStuff 3 == 19

19.4 Useful Monadic Functions (Control.Monad)

import Control.Monad
mapM print [1, 2, 3]                    -- run actions, collect results (traverse)
mapM_ print [1, 2, 3]                   -- run actions, discard results (traverse_)
forM_ [1 .. 3] $ \i -> print (i * i)    -- flipped mapM_
replicateM 2 "ab"                       -- ["aa","ab","ba","bb"]
replicateM_ 3 (putStrLn "hi")
when True (putStrLn "printed")
unless False (putStrLn "also printed")
foldM (\acc x -> if x > 0 then Just (acc + x) else Nothing) 0 [1, 2, 3]   -- Just 6
filterM (const [True, False]) [1, 2]    -- powerset: [[1,2],[1],[2],[]]
zipWithM_ (\i s -> putStrLn (show i ++ ": " ++ s)) [1 ..] ["a", "b"]
join (Just (Just 3))                    -- Just 3
forever (getLine >>= putStrLn)          -- loop forever (until an exception)
-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c   -- Kleisli composition
safeRoot = (\x -> if x < 0 then Nothing else Just (sqrt x)) >=> (\y -> Just (y * 2))