The MonadUnliftIO typeclass solves the problem of running computations that require IO (like withBinaryFile or bracket) inside a monad transformer stack (like ReaderT Env IO) without manually unwrapping constructors or being limited by MonadIO.
While MonadIO allows you to lift IO actions into your monad using liftIO, it cannot handle functions where IO appears in a negative/argument position (e.g., a callback function that expects IO a but you want to provide m a).
MonadUnliftIO provides the withRunInIO method, which allows you to capture a function (runInIO) that can execute any computation from your monad m as if it were in IO. This is the "unlift" operation.
import Control.Monad.IO.Unlift
-- Example: Unlifting a function that takes a callback in IO
myWithBinaryFile
:: MonadUnliftIO m
=> FilePath
-> IOMode
-> (Handle -> m a)
-> m a
myWithBinaryFile fp mode inner =
withRunInIO $ \runInIO ->
withBinaryFile
fp
mode
(\h -> runInIO (inner h))