Reader
Table of Contents
The Reader type models computation that can read from a shared, read-only environment, for example, application configuration, dependencies, logging settings
In C, we can create a Config struct and pass it everywhere whenever needed. In Haskell, the Reader pattern packages this repeated environment passing into a computational context.
Conceptually, it can be regarded as
newtype Reader r a = Reader { runReader :: r -> a }
Reader r a models an operation of r -> a within the monad.
1. Main Operations
We first import it to build dependencies by including mtl hackage. Then import
import Control.Monad.Reader
1.1. ask Function
The ask function retrieves the entire environment.
ask :: Reader r r
-- e.g.
describeConfig :: Reader Config String
describeConfig = do
config <- ask
return $
host config
++ ":"
++ show (port config)
1.2. asks Function
asks accepts a query function, and extracts from the environment.
asks :: (r -> a) -> Reader r a
-- e.g.
getPort :: Reader Config Int
getPort = asks port
1.3. local Function
local function can temporarily modifies the environment for one sub-computation.
local :: (r -> r') -> Reader r a -> Reader r' a
-- e.g.
queryDebug = do
enabled <- asks debugMode
return $
if enabled
then "Debug enabled"
else "Debug disabled"
withoutDebug :: Reader Config String
withoutDebug =
local
(\config -> config { debugMode = False })
debugStatus
runReader withoutDebug config -- => "Debug disabled"
1.4. runReader Function
Given all operations to do and an initial environment, runReader runs them and produces final result.
runReader :: Reader r a -> r -> a
2. Reader as Monad
The monadic operation is
(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
-- which is exactly
(r -> a) -> (a -> r -> b) -> (r -> b)
which can model that, under the fixed environment r, if I have an operation that takes a as input, r as environmental support, and produces b as result, then the overall result is Reader r b.
The second expansion is more clear. In the environment r (which is provided in both arguments), the first can produce a, and the second requires a to produce b, then the overall result is b. Since r is not provided initially, thus the final result is a function r -> b.