The ReaderT Design Pattern (2017)
The ReaderT design pattern is a high-level approach to structuring Haskell programs. It is not a universally accepted pattern in the Haskell community, but it is one of the best practices recommended by FP Complete. The pattern addresses common concerns such as global variables, mutable globals, and using unsafePerformIO.
To illustrate the pattern, consider an application that needs to configure its logging level. There are three common approaches: (1) tempting but problematic due to conditional compilation leading to build failures; (2) using unsafePerformIO, which may seem safe but has its own issues; and (3) defining an Env data type and threading it through the application.
The latter approach is preferred, as it eliminates the potential pain of mechanical code rewriting and reduces the likelihood of resorting to hacks like CPP code or global variables.
Another use case for the pattern is initializing resources such as random number generators, log message handles, database pools, or temporary directories. These tasks are more logical to perform inside the main function rather than from a global position. If deferring initialization until the first use is desired, the runOnce approach can be employed.
Regarding mutability, Haskell enthusiasts often argue that purity is paramount and that mutability is the devil. However, the ReaderT pattern acknowledges the need for mutability in certain situations. Mutable references can provide advantages like exception-survival, false purity (accepting the presence of mutable variables), and better concurrency handling compared to WriterT or StateT.
This is particularly useful in applications like Yesod, where response headers need to be set even if a response fails with an error like notFound.
Written by urgent.news from Lobsters's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.