What is the difference between the two:
recompile :: MonadIO m => Bool -> m Bool
recompile :: Bool -> IO Bool
                The types forall m. MonadIO m => Bool -> m Bool and Bool -> IO Bool are isomorphic:
{-# LANGUAGE RankNTypes #-}
import Control.Monad.IO.Class
from :: (forall m. MonadIO m => Bool -> m Bool) -> (Bool -> IO Bool)
from act = act
to :: (Bool -> IO Bool) -> (forall m. MonadIO m => Bool -> m Bool)
to act = liftIO . act
Not all types involving IO are isomorphic to a version that replaces IO with a constrained m, though.
Generally, one uses MonadIO m instead of m ~ IO when they feel like scattering liftIO through calling code will be annoying or when there are other mtl-style constraints needed on m; and uses m ~ IO instead of MonadIO m when going for simplicity of API (where MonadIO m contributes to complexity and therefore is not desirable) or when dealing with forking or exception handling (where MonadIO m is not possible for the reasons discussed in the linked question).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With