| Prev: The State monad | TOC: Contents | Next: The Writer monad |
| Computation type: | Computations which read values from a shared environment. |
|---|---|
| Binding strategy: | Monad values are functions from the environment to a value. The bound function is applied to the bound value, and both have access to the shared environment. |
| Useful for: | Maintaining variable bindings, or other shared environment. |
| Zero and plus: | None. |
| Example type: | Reader [(String,Value)] a |
Some programming problems require computations within a shared environment (such as a set of variable bindings). These computations typically read values from the environment and sometimes execute sub-computations in a modified environment (with new or shadowing bindings, for example), but they do not require the full generality of the State monad.
The Reader monad is specifically designed for these types of computations and is often a clearer and easier mechanism than using the State monad.
The definition shown here uses multi-parameter type classes and funDeps, which are not standard Haskell 98. It is not necessary to fully understand these details to make use of the Reader monad.
newtype Reader e a = Reader { runReader :: (e -> a) }
instance Monad (Reader e) where
return a = Reader $ \e -> a
(Reader r) >>= f = Reader $ \e -> f (r e) e
|
Values in the Reader monad are functions from an environment to a value.
To extract the final value from a computation in the Reader monad,
you simply apply (runReader reader) to an environment value.
The return function creates a Reader that ignores
the environment and produces the given value. The binding operator produces
a Reader that uses the environment to extract the value its
left-hand side and then applies the bound function to that value in the
same environment.
class MonadReader e m | m -> e where
ask :: m e
local :: (e -> e) -> m a -> m a
instance MonadReader (Reader e) where
ask = Reader id
local f c = Reader $ \e -> runReader c (f e)
asks :: (MonadReader e m) => (e -> a) -> m a
asks sel = ask >>= return . sel
|
The MonadReader class provides a number of convenience functions
that are very useful when working with a Reader monad. The ask
function retrieves the environment and the local function
executes a computation in a modified environment. The asks
function is a convenience function that retrieves a function of the
current environment, and is typically used with a selector or lookup function.
Consider the problem of instantiating templates which contain variable
substitutions and included templates. Using the Reader monad, we can
maintain an environment of all known templates and all known variable
bindings. Then, when a variable substitution is encountered, we can
use the asks function to lookup the value of the variable.
When a template is included with new variable definitions, we can use the
local function to resolve the template in a modified environment
that contains the additional variable bindings.
| Code available in example16.hs |
|---|
-- This the abstract syntax representation of a template
-- Text Variable Quote Include Compound
data Template = T String | V Template | Q Template | I Template [Definition] | C [Template]
data Definition = D Template Template
-- Our environment consists of an association list of named templates and
-- an association list of named variable values.
data Environment = Env {templates::[(String,Template)],
variables::[(String,String)]}
-- lookup a variable from the environment
lookupVar :: String -> Environment -> Maybe String
lookupVar name env = lookup name (variables env)
-- lookup a template from the environment
lookupTemplate :: String -> Environment -> Maybe Template
lookupTemplate name env = lookup name (templates env)
-- add a list of resolved definitions to the environment
addDefs :: [(String,String)] -> Environment -> Environment
addDefs defs env = env {variables = defs ++ (variables env)}
-- resolve a Definition and produce a (name,value) pair
resolveDef :: Definition -> Reader Environment (String,String)
resolveDef (D t d) = do name <- resolve t
value <- resolve d
return (name,value)
-- resolve a template into a string
resolve :: Template -> Reader Environment (String)
resolve (T s) = return s
resolve (V t) = do varName <- resolve t
varValue <- asks (lookupVar varName)
return $ maybe "" id varValue
resolve (Q t) = do tmplName <- resolve t
body <- asks (lookupTemplate tmplName)
return $ maybe "" show body
resolve (I t ds) = do tmplName <- resolve t
body <- asks (lookupTemplate tmplName)
case body of
Just t' -> do defs <- mapM resolveDef ds
local (addDefs defs) (resolve t')
Nothing -> return ""
resolve (C ts) = (liftM concat) (mapM resolve ts)
|
To use the Reader monad to resolve a template t into a
String, you simply need to do runReader (resolve t) env.
| Prev: The State monad | TOC: Contents | Next: The Writer monad |