Common Monads: Reader, Writer, State

These come from the mtl/transformers packages. State s a is a newtype around s -> (a, s). Reader is a function r -> a. Writer pairs a result with a Monoid.

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State

-- Reader: read-only shared environment (configuration, dependencies)
data Config = Config {verbose :: Bool, appName :: String}

logMsg :: String -> Reader Config String
logMsg msg = do
  cfg <- ask
  name <- asks appName
  pure (if verbose cfg then "[" ++ name ++ "] " ++ msg else msg)

-- runReader (logMsg "started") (Config True "demo") == "[demo] started"
-- local (\c -> c {verbose = False}) action  -- run with a modified environment

-- Writer: accumulate output (logs, metrics) alongside a result
collatz :: Int -> Writer [Int] Int
collatz 1 = tell [1] >> pure 0
collatz n = do
  tell [n]
  steps <- collatz (if even n then n `div` 2 else 3 * n + 1)
  pure (steps + 1)
-- runWriter (collatz 6) == (8,[6,3,10,5,16,8,4,2,1])
-- (Writer is lazy and can leak memory; for logging in real apps prefer a logging library or StateT)

-- State: threading state through computations
type Stack = [Int]

push :: Int -> State Stack ()
push x = modify (x :)

pop :: State Stack (Maybe Int)
pop = do
  s <- get
  case s of
    [] -> pure Nothing
    (x : xs) -> put xs >> pure (Just x)

stackOps :: State Stack (Maybe Int)
…