title: How to understand `MonadUnliftIO`'s requirement of "no stateful monads"?
body: <p>I've looked over <a href="https://www.fpcomplete.com/blog/2017/06/tale-of-two-brackets" rel="noreferrer">https://www.fpcomplete.com/blog/2017/06/tale-of-two-brackets</a>, though skimming some parts, and I still don't quite understand the core issue "<code>StateT</code> is bad, <code>IO</code> is OK", other than vaguely getting the sense that Haskell allows one to write bad <code>StateT</code> monads (or in the ultimate example in the article, <code>MonadBaseControl</code> instead of <code>StateT</code>, I think).</p>
  
  <p>In the haddocks, the following law must be satisfied:</p>
  
  <pre><code>askUnliftIO &gt;&gt;= (\u -&gt; liftIO (unliftIO u m)) = m
  </code></pre>
  
  <p>So this appears to be saying that state is not mutated in the monad <code>m</code> when using <code>askUnliftIO</code>. But to my mind, in <code>IO</code>, the entire world can be the state. I could be reading and writing to a text file on disk, for instance.</p>
  
  <p>To quote <a href="https://www.fpcomplete.com/blog/2017/06/readert-design-pattern" rel="noreferrer">another article by Michael</a>, </p>
  
  <blockquote>
    <p>False purity We say WriterT and StateT are pure, and technically they
    are. But let's be honest: if you have an application which is entirely
    living within a StateT, you're not getting the benefits of restrained
    mutation that you want from pure code. May as well call a spade a
    spade, and accept that you have a mutable variable.</p>
  </blockquote>
  
  <p>This makes me think this is indeed the case: with IO we are being honest, with <code>StateT</code>, we are not being honest about mutability ... but that seems another issue than what the law above is trying to show; after all, <code>MonadUnliftIO</code> is assuming <code>IO</code>. I'm having trouble understanding conceptually how <code>IO</code> is more restrictive than something else.</p>
  
  <p><strong>Update 1</strong></p>
  
  <p>After sleeping (some), I am still confused but am gradually getting less so as the day wears on. I worked out the law proof for <code>IO</code>. I realized the presence of <code>id</code> in the README. In particular,</p>
  
  <pre><code>instance MonadUnliftIO IO where
    askUnliftIO = return (UnliftIO id)
  </code></pre>
  
  <p>So <code>askUnliftIO</code> would appear to return an <code>IO (IO a)</code> on an <code>UnliftIO m</code>.</p>
  
  <pre><code>Prelude&gt; fooIO = print 5
  Prelude&gt; :t fooIO
  fooIO :: IO ()
  Prelude&gt; let barIO :: IO(IO ()); barIO = return fooIO
  Prelude&gt; :t barIO
  barIO :: IO (IO ())
  </code></pre>
  
  <p>Back to the law, it really appears to be saying that state is not mutated in the monad <code>m</code> when doing a round trip on the transformed monad (<code>askUnliftIO</code>), where the round trip is <code>unLiftIO</code> -> <code>liftIO</code>.</p>
  
  <p>Resuming the example above, <code>barIO :: IO ()</code>, so if we do <code>barIO &gt;&gt;= (u -&gt; liftIO (unliftIO u m))</code>, then <code>u :: IO ()</code> and <code>unliftIO u == IO ()</code>, then <code>liftIO (IO ()) == IO ()</code>. **So since everything has basically been applications of <code>id</code> under the hood, we can see that no state was changed, even though we are using <code>IO</code>. Crucially, I think, what is important is that the value in <code>a</code> is never run, nor is any other state modified, as a result of using <code>askUnliftIO</code>. If it did, then like in the case of <code>randomIO :: IO a</code>, we would not be able to get the same value had we not run <code>askUnliftIO</code> on it. (Verification attempt 1 below)</p>
  
  <p><em>But</em>, it still seems like we <em>could</em> do the same for other Monads, even if they do maintain state. But I also see how, for some monads, we may not be able to do so. Thinking of a contrived example: each time we access the value of type <code>a</code> contained in the stateful  monad, some internal state is changed. </p>
  
  <p><strong>Verification attempt 1</strong></p>
  
  <pre><code>&gt; fooIO &gt;&gt; askUnliftIO
  5
  &gt; fooIOunlift = fooIO &gt;&gt; askUnliftIO
  &gt; :t fooIOunlift
  fooIOunlift :: IO (UnliftIO IO)
  &gt; fooIOunlift
  5
  </code></pre>
  
  <p>Good so far, but confused about why the following occurs:</p>
  
  <pre><code> &gt; fooIOunlift &gt;&gt;= (\u -&gt; unliftIO u)
  
  &lt;interactive&gt;:50:24: error:
      * Couldn't match expected type `IO b'
                    with actual type `IO a0 -&gt; IO a0'
      * Probable cause: `unliftIO' is applied to too few arguments
        In the expression: unliftIO u
        In the second argument of `(&gt;&gt;=)', namely `(\ u -&gt; unliftIO u)'
        In the expression: fooIOunlift &gt;&gt;= (\ u -&gt; unliftIO u)
      * Relevant bindings include
          it :: IO b (bound at &lt;interactive&gt;:50:1)
  </code></pre>