mtl is a Haskell library style for expressing computations through an abstract monad and type-class constraints rather than committing every function to IO. This keeps an effectful operation usable across compatible monadic contexts. section 11.1.1
The design answers a practical interface problem: database libraries expose different capabilities, so a single universal interface becomes too broad. An mtl-style signature selects the useful effect boundary while leaving the concrete monad to the caller. section 11.1.1
The main patterns are:
- Use constraints such as MonadIO m to require only the effect needed by an operation; specializing m to IO permits ordinary IO use. section 11.1.1
- Represent a service as a type class, then select an implementation through instances and a type-level selector. section 13.2.6
- Inject the selected service into business logic while retaining an effectful application monad such as AppL. section 13.2.6
Database bindings
A RocksDB binding exposes get :: MonadIO m => DB -> ReadOptions -> ByteString -> m (Maybe ByteString), allowing the operation to run in any monad satisfying MonadIO. Redis uses a custom Redis context, with Redis for query declarations and runRedis for execution against IO. section 11.1.1
Service selection
An AstroService class supports HTTP and TCP interpreters. Business logic receives a ReportChannel and a type selector, while the runtime chooses meteorsApp @(HttpAstroService) or meteorsApp @(TcpAstroService). The approach is explicit but leaks channel details and can fail on mismatches. section 13.2.6
Listing 11.1 RocksDB bindings
data DB = DB RocksDBPtr Options' #1 get #2 :: MonadIO m #3 => DB #4 -> ReadOptions #5 -> ByteString #6 -> m (Maybe ByteString) #7
Inspect the compact RocksDB signature to see how an abstract monad and MonadIO constraint separate the operation from its final IO interpretation. section 11.1.1
