As a bonus, here’s a Monad
that doesn’t let you escape resources out of the do
block, similar to how it is done with runST
:
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE BlockArguments #-}
module ScopedCont where
import System.IO qualified
import Control.Monad.Codensity
import UnliftIO (MonadIO, IOMode (WriteMode), liftIO)
import Data.Kind
import Control.Monad.Trans (MonadTrans, lift)
newtype SCod s (m :: k -> Type) a = MkSCod {unSCod :: forall b. (a -> m b) -> m b}
deriving (Functor, Applicative, Monad) via (Codensity m)
deriving MonadTrans via Codensity
instance MonadIO m => MonadIO (SCod s m) where
liftIO = lift . liftIO
runSCod :: (Applicative m) => (forall s. SCod s m r) -> m r
runSCod k = unSCod k pure
newtype Handle s = MkHandle {unsafeGetHandle :: System.IO.Handle}
withFile :: FilePath -> System.IO.IOMode -> SCod s IO (Handle s)
withFile fp md = MkSCod (\k -> System.IO.withFile fp md (k . MkHandle))
hPutStrLn :: Handle s -> String -> SCod s IO ()
hPutStrLn (MkHandle hdl) = liftIO $ System.IO.hPutStrLn hdl
x :: IO ()
x = runSCod do
hdl <- withFile "bla" WriteMode
liftIO $ hPutStrLn hdl "test"
y :: IO (Handle s)
y = runSCod do
withFile "bla" WriteMode