If a monad is like giving some additional context to a value:<p><pre><code>    Maybe a: &quot;Either it&#x27;s a value or nothing&quot;
    [a]: &quot;The value was obtained from a list of possible values&quot;
    IO a: &quot;The value was obtained from a database somewhere&quot;
    Except ErrorType a: &quot;The value is either an error message or a real value&quot;
</code></pre>
, then a monad transformer takes an existing monad and augments it with even more context:<p><pre><code>    ExceptT ErrorType IO a: &quot;this value was obtained from a database somewhere, and also maybe there was an error message&quot;
    StateT ConnectionPool IO a: &quot;this web server response value was calculated using a connection obtained from the server&#x27;s stateful connection pool, and a database connection&quot;
</code></pre>
Most monads like State are actually defined as the monad transformer applied to the Identity monad, which has no special context at all:<p><pre><code>    State stateType returnType = StateT stateType Identity returnType
</code></pre>
Additionally, IO is very common to bury at the bottom of one of these stacks, so there is &#x27;liftIO&#x27; to run an IO action in a monad transformer stack. So your web server can just do:<p><pre><code>    liftIO $ putStrLn &quot;Hello World&quot;
</code></pre>
if you need a quick log message. You need liftIO, because &#x27;putStrLn &quot;Hello World&quot;&#x27; is of type IO (), and the web server needs type &#x27;StateT ConnectionPool IO ()&#x27;.<p>Note: IO does more than just databases, of course.