{-# LANGUAGE DeriveFunctor, ExistentialQuantification, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeApplications, TypeOperators, UndecidableInstances #-}
module Control.Effect.Resource
( Resource(..)
, bracket
, bracketOnError
, finally
, onException
, runResource
, withResource
, ResourceC(..)
) where
import Control.Applicative (Alternative(..))
import Control.Effect.Carrier
import Control.Effect.Reader
import Control.Effect.Sum
import qualified Control.Exception as Exc
import Control.Monad (MonadPlus(..))
import Control.Monad.Fail
import Control.Monad.IO.Class
import Control.Monad.IO.Unlift
import Control.Monad.Trans.Class
data Resource m k
= forall resource any output . Resource (m resource) (resource -> m any) (resource -> m output) (output -> k)
| forall resource any output . OnError (m resource) (resource -> m any) (resource -> m output) (output -> k)
deriving instance Functor (Resource m)
instance HFunctor Resource where
hmap f (Resource acquire release use k) = Resource (f acquire) (f . release) (f . use) k
hmap f (OnError acquire release use k) = OnError (f acquire) (f . release) (f . use) k
instance Effect Resource where
handle state handler (Resource acquire release use k) = Resource (handler (acquire <$ state)) (handler . fmap release) (handler . fmap use) (handler . fmap k)
handle state handler (OnError acquire release use k) = OnError (handler (acquire <$ state)) (handler . fmap release) (handler . fmap use) (handler . fmap k)
bracket :: (Member Resource sig, Carrier sig m)
=> m resource
-> (resource -> m any)
-> (resource -> m a)
-> m a
bracket acquire release use = send (Resource acquire release use pure)
bracketOnError :: (Member Resource sig, Carrier sig m)
=> m resource
-> (resource -> m any)
-> (resource -> m a)
-> m a
bracketOnError acquire release use = send (OnError acquire release use pure)
finally :: (Member Resource sig, Carrier sig m)
=> m a
-> m b
-> m a
finally act end = bracket (pure ()) (const end) (const act)
onException :: (Member Resource sig, Carrier sig m)
=> m a
-> m b
-> m a
onException act end = bracketOnError (pure ()) (const end) (const act)
runResource :: (forall x . m x -> IO x)
-> ResourceC m a
-> m a
runResource handler = runReader (Handler handler) . runResourceC
withResource :: MonadUnliftIO m
=> ResourceC m a
-> m a
withResource r = withRunInIO (\f -> runHandler (Handler f) r)
newtype ResourceC m a = ResourceC { runResourceC :: ReaderC (Handler m) m a }
deriving (Alternative, Applicative, Functor, Monad, MonadFail, MonadIO, MonadPlus)
instance MonadUnliftIO m => MonadUnliftIO (ResourceC m) where
askUnliftIO = ResourceC . ReaderC $ \(Handler h) ->
withUnliftIO $ \u -> pure (UnliftIO $ \r -> unliftIO u (runResource h r))
instance MonadTrans ResourceC where
lift = ResourceC . lift
newtype Handler m = Handler (forall x . m x -> IO x)
runHandler :: Handler m -> ResourceC m a -> IO a
runHandler h@(Handler handler) = handler . runReader h . runResourceC
instance (Carrier sig m, MonadIO m) => Carrier (Resource :+: sig) (ResourceC m) where
eff (L (Resource acquire release use k)) = do
handler <- ResourceC ask
a <- liftIO (Exc.bracket
(runHandler handler acquire)
(runHandler handler . release)
(runHandler handler . use))
k a
eff (L (OnError acquire release use k)) = do
handler <- ResourceC ask
a <- liftIO (Exc.bracketOnError
(runHandler handler acquire)
(runHandler handler . release)
(runHandler handler . use))
k a
eff (R other) = ResourceC (eff (R (handleCoercible other)))