Skip to content

Instantly share code, notes, and snippets.

@dcastro
Last active November 14, 2025 20:23
Show Gist options
  • Select an option

  • Save dcastro/455d8273addd488792d263358119ed76 to your computer and use it in GitHub Desktop.

Select an option

Save dcastro/455d8273addd488792d263358119ed76 to your computer and use it in GitHub Desktop.
MonadBaseControl - lifting `finally`
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImportQualifiedPost #-}
import Control.Exception (SomeException, throwIO, try)
import Control.Exception.Lifted qualified as Lifted
import Control.Monad.Base (liftBase)
import Control.Monad.Except (ExceptT, throwError)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Control (MonadBaseControl, liftBaseWith, restoreM)
finally' :: (MonadBaseControl IO m) => m a -> m b -> m a
finally' ma mb = Lifted.mask $ \restore -> do
a <- liftBaseWith $ \runInBase ->
try (runInBase (restore ma))
case a of
Left e -> mb *> liftBase (throwIO (e :: SomeException))
Right s -> restoreM s <* mb
{- |
>>> import Control.Monad.Except (runExceptT)
>>> runExceptT finallyTest1
In action
Left "error in action"
-}
finallyTest1 :: ExceptT String IO ()
finallyTest1 =
finally'
( do
liftIO $ putStrLn "In action"
throwError "error in action"
)
( do
-- BUG: this block will never be run
liftIO $ putStrLn "In cleanup"
throwError "error in cleanup"
)
{- |
>>> import Control.Monad.Except (runExceptT)
>>> runExceptT finallyTest2
In action
In cleanup
*** Exception: user error (error in cleanup)
-}
finallyTest2 :: ExceptT String IO ()
finallyTest2 =
finally'
( do
liftIO $ putStrLn "In action"
liftIO $ fail "error in action"
)
( do
liftIO $ putStrLn "In cleanup"
liftIO $ fail "error in cleanup"
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment