# Monad Transformers and Application Structure — Haskell

Source: https://www.geekswithgeeks.com/en/haskell/r-mtl

> Combine Reader, State and IO with mtl, and know the alternatives.

## Stacking effects

Real applications need several effects at once: read configuration, keep state, log and perform I/O. **Monad transformers** stack these capabilities: **`ReaderT Config IO`** adds a read-only environment to IO, **`StateT s m`** adds state, **`ExceptT e m`** adds typed errors, and **`WriterT`** adds output (though it is rarely recommended because of space leaks). The **`mtl`** library provides type classes such as **`MonadReader`**, **`MonadState`**, **`MonadError`** and **`MonadIO`** so functions can state the capabilities they need (`(MonadReader Config m, MonadIO m) => m Int`) and `ask`, `get` and `liftIO` work through the stack without manual lifting. A popular, simple architecture is the **ReaderT design pattern**: a single `ReaderT Env IO` with an environment record holding configuration, connection pools and mutable references (`IORef`, `TVar`), avoiding deep stacks. **Effect system** libraries (such as `effectful`, `polysemy` or `bluefin`) offer alternatives with different trade-offs. Keep business logic pure where possible and use the application monad only for orchestration.

## A small application monad with mtl

ReaderT for configuration over StateT for a log over IO.

```haskell
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Reader (ReaderT, ask, runReaderT)
import Control.Monad.State (StateT, modify, runStateT)

data Config = Config { gstPercent :: Int, freeShippingFrom :: Int }

type App = ReaderT Config (StateT [String] IO)

logMsg :: String -> App ()
logMsg m = modify (++ [m])              -- mtl lifts this through ReaderT automatically

priceOrder :: Int -> App Int
priceOrder subtotal = do
  cfg <- ask
  let gst      = subtotal * gstPercent cfg `div` 100
      shipping = if subtotal >= freeShippingFrom cfg then 0 else 4900
  logMsg ("gst " ++ show gst ++ ", shipping " ++ show shipping)
  liftIO (putStrLn ("priced order of " ++ show subtotal))
  pure (subtotal + gst + shipping)

main :: IO ()
main = do
  let cfg = Config { gstPercent = 18, freeShippingFrom = 100000 }
  (total, logs) <- runStateT (runReaderT (priceOrder 50000) cfg) []
  print total                            -- 63900
  mapM_ putStrLn logs                    -- gst 9000, shipping 4900
```

## Layers of a delivery bag

A transformer stack is a delivery bag with layers: the outer pocket holds the address card (Reader), the middle a running receipt (State), and the core is the actual trip through the city (IO). Each layer adds a capability without the others needing to know about it.

**Quiz:** What does ReaderT Config IO add to plain IO?

- [ ] Mutable state
- [x] Access to a shared, read-only Config value via ask
- [ ] Exceptions
- [ ] Parallelism

*Answer:* Access to a shared, read-only Config value via ask. ReaderT threads an environment through computations implicitly.
