I had to read Real World Haskell&#x27;s chapter twice: <a href="http:&#x2F;&#x2F;book.realworldhaskell.org&#x2F;read&#x2F;monads.html" rel="nofollow">http:&#x2F;&#x2F;book.realworldhaskell.org&#x2F;read&#x2F;monads.html</a> to figure it out.<p>It&#x27;s essentially a hidden argument that&#x27;s passed around between the environment (the stuff in &quot;do ... &quot;) and the actions (the stuff called in &quot;do ...&quot;, e.g, &quot;x &lt;- foo&quot;, foo would be the action).  The argument is always the same type, and the each action can create a new one based off the old one.  So you have IO, which is essentially &quot;all interactions with the outside non-pure world&quot;, and the versions of it are &quot;the world before I did this action&quot; and &quot;the world after I did this action&quot;.<p>Stuff that doesn&#x27;t use the hidden arg doesn&#x27;t have the action type (e.g., IO ()), so it has to be lifted, which essentially passes the old world state verbatim to the next step.