module Data.Acquire.Internal
( Acquire (..)
, Allocated (..)
, with
, mkAcquire
, ReleaseType (..)
, mkAcquireType
) where
import Control.Applicative (Applicative (..))
import Control.Monad.IO.Unlift (MonadIO (..), MonadUnliftIO, withRunInIO)
import qualified Control.Exception as E
import Data.Typeable (Typeable)
import Control.Monad (liftM, ap)
import qualified Control.Monad.Catch as C ()
data ReleaseType = ReleaseEarly
| ReleaseNormal
| ReleaseException
deriving (Show, Read, Eq, Ord, Enum, Bounded, Typeable)
data Allocated a = Allocated !a !(ReleaseType -> IO ())
newtype Acquire a = Acquire ((forall b. IO b -> IO b) -> IO (Allocated a))
deriving Typeable
instance Functor Acquire where
fmap = liftM
instance Applicative Acquire where
pure a = Acquire (\_ -> return (Allocated a (const $ return ())))
(<*>) = ap
instance Monad Acquire where
return = pure
Acquire f >>= g' = Acquire $ \restore -> do
Allocated x free1 <- f restore
let Acquire g = g' x
Allocated y free2 <- g restore `E.onException` free1 ReleaseException
return $! Allocated y (\rt -> free2 rt `E.finally` free1 rt)
instance MonadIO Acquire where
liftIO f = Acquire $ \restore -> do
x <- restore f
return $! Allocated x (const $ return ())
mkAcquire :: IO a
-> (a -> IO ())
-> Acquire a
mkAcquire create free = Acquire $ \restore -> do
x <- restore create
return $! Allocated x (const $ free x)
mkAcquireType
:: IO a
-> (a -> ReleaseType -> IO ())
-> Acquire a
mkAcquireType create free = Acquire $ \restore -> do
x <- restore create
return $! Allocated x (free x)
with :: MonadUnliftIO m
=> Acquire a
-> (a -> m b)
-> m b
with (Acquire f) g = withRunInIO $ \run -> E.mask $ \restore -> do
Allocated x free <- f restore
res <- restore (run (g x)) `E.onException` free ReleaseException
free ReleaseNormal
return res