ReaderT solves a dependency-passing problem: business logic needs an environment of services, but passing a handle through every function creates coupling and noisy interfaces. It stores that handle in an immutable Reader environment surrounding the underlying application monad. section 13.2 section 13.2.3
Define an AppEnv containing service functions, then wrap the application language in ReaderT: type AppRT a = ReaderT AppEnv AppL a. Business logic retrieves the environment with ask and calls underlying AppL actions through lift. section 13.2.3
Injecting an application environment
Construct AppEnv from a reporting channel, run the application with runReaderT meteorsApp appEnv, and unwrap the resulting AppL computation for the framework. This keeps dependency formation at the boundary while the scenario reads services from its context. section 13.2.3
The practical trade-offs are:
- ReaderT removes the explicit handle from application arguments and supports dependency inversion through an injected environment. section 13.2.3 section 13.3
- Nested AppRT and AppL layers require lift inside business logic and unwrapping at the application boundary. section 13.2.3
- The resulting boilerplate may outweigh the benefit, so the simpler Service Handle pattern can be preferable. section 13.2.3
data AppEnv = AppEnv
{ meteorReporter
:: MeteorTemplate
-> AppL (Either ReportingError MeteorId)
, asteroidReporter
:: AsteroidTemplate
-> AppL (Either ReportingError AsteroidId)
}
Browse the AppEnv definition to see how reporting dependencies are represented as functions returning AppL computations. section 13.2.3
Listing 13.9 DIP-compliant code with ReaderT
meteorsApp :: AppRT ()
meteorsApp = do
AppEnv reporter _ <- ask #1
eResult <- lift (reporter (MeteorTemplate 1 1 1 1)) #2
case eResult of
Right meteorId -> ...
Left err -> ...
Browse the complete ReaderT scenario to see ask retrieve the environment and lift invoke an underlying AppL action. section 13.2.3

