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.

Date: 2026-07-31 Fri

Author: ArcaLunar