{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -Wno-orphans #-} -- | Resource management via 'R.MonadResource'. module Effectful.Resource ( -- * Effect Resource -- ** Handlers , runResource -- * Registering and releasing resources , R.allocate , R.allocate_ , R.register , R.release , R.unprotect -- * Internal state , R.InternalState , getInternalState , runInternalState , R.createInternalState , R.closeInternalState ) where import Control.Exception import qualified Control.Monad.Trans.Resource as R import qualified Control.Monad.Trans.Resource.Internal as RI import Effectful import Effectful.Dispatch.Static import Effectful.Dispatch.Static.Primitive -- | Provide the ability to use the 'R.MonadResource' instance of 'Eff'. data Resource :: Effect type instance DispatchOf Resource = Static WithSideEffects newtype instance StaticRep Resource = Resource R.InternalState -- | Run the resource effect. runResource :: IOE :> es => Eff (Resource : es) a -> Eff es a runResource m = unsafeEff $ \es0 -> do istate <- R.createInternalState mask $ \unmask -> do es <- consEnv (Resource istate) dummyRelinker es0 a <- unmask (unEff m es) `catch` \e -> do unconsEnv es RI.stateCleanupChecked (Just e) istate throwIO e unconsEnv es RI.stateCleanupChecked Nothing istate pure a ---------------------------------------- -- Internal state -- | Get the 'R.InternalState' of the current 'Resource' effect. getInternalState :: Resource :> es => Eff es R.InternalState getInternalState = do Resource istate <- getStaticRep pure istate -- | Run the 'Resource' effect with existing 'R.InternalState'. -- -- /Note:/ the 'R.InternalState' will not be closed at the end. runInternalState :: IOE :> es => R.InternalState -> Eff (Resource : es) a -> Eff es a runInternalState istate = evalStaticRep (Resource istate) ---------------------------------------- -- Orphan instance instance (IOE :> es, Resource :> es) => R.MonadResource (Eff es) where liftResourceT (RI.ResourceT m) = unsafeEff $ \es -> do getEnv es >>= \(Resource istate) -> m istate