polysemy-1.9.2.0: src/Polysemy/Resource.hs
{-# LANGUAGE TemplateHaskell #-}
-- | Description: The 'Resource' effect, providing bracketing functionality
module Polysemy.Resource
( -- * Effect
Resource (..)
-- * Actions
, bracket
, bracket_
, bracketOnError
, finally
, onException
-- * Interpretations
, runResource
, resourceToIOFinal
) where
import qualified Control.Exception as X
import Polysemy
import Polysemy.Final
------------------------------------------------------------------------------
-- | An effect capable of providing 'X.bracket' semantics. Interpreters for this
-- will successfully run the deallocation action even in the presence of other
-- short-circuiting effects.
data Resource m a where
-- | Allocate a resource, use it, and clean it up afterwards.
Bracket
:: m a
-- Action to allocate a resource.
-> (a -> m c)
-- Action to cleanup the resource. This is guaranteed to be
-- called.
-> (a -> m b)
-- Action which uses the resource.
-> Resource m b
-- | Allocate a resource, use it, and clean it up afterwards if an error occurred.
BracketOnError
:: m a
-- Action to allocate a resource.
-> (a -> m c)
-- Action to cleanup the resource. This will only be called if the
-- "use" block fails.
-> (a -> m b)
-- Action which uses the resource.
-> Resource m b
makeSem ''Resource
------------------------------------------------------------------------------
-- | A variant of 'bracket' where the return value from the first computation
-- is not required.
--
-- cf. 'Control.Exception.bracket' and 'Control.Exception.bracket_'
--
-- @since 1.5.0.0
bracket_
:: Member Resource r
=> Sem r a -- ^ computation to run first
-> Sem r b -- ^ computation to run last (even if an exception was raised)
-> Sem r c -- ^ computation to run in-between
-> Sem r c
bracket_ begin end act = bracket begin (const end) (const act)
------------------------------------------------------------------------------
-- | Like 'bracket', but for the simple case of one computation to run
-- afterward.
--
-- @since 0.4.0.0
finally
:: Member Resource r
=> Sem r a -- ^ computation to run first
-> Sem r b -- ^ computation to run afterward (even if an exception was raised)
-> Sem r a
finally act end = bracket (pure ()) (const end) (const act)
------------------------------------------------------------------------------
-- | Like 'bracketOnError', but for the simple case of one computation to run
-- afterward.
--
-- @since 0.4.0.0
onException
:: Member Resource r
=> Sem r a -- ^ computation to run first
-> Sem r b -- ^ computation to run afterward if an exception was raised
-> Sem r a
onException act end = bracketOnError (pure ()) (const end) (const act)
------------------------------------------------------------------------------
-- | Run a 'Resource' effect in terms of 'X.bracket' through final 'IO'
--
-- /Beware/: Effects that aren't interpreted in terms of 'IO'
-- will have local state semantics in regards to 'Resource' effects
-- interpreted this way. See 'Final'.
--
-- @since 1.2.0.0
resourceToIOFinal :: Member (Final IO) r
=> Sem (Resource ': r) a
-> Sem r a
resourceToIOFinal = interpretFinal $ \case
Bracket alloc dealloc use -> do
a <- runS alloc
d <- bindS dealloc
u <- bindS use
pure $ X.bracket a d u
BracketOnError alloc dealloc use -> do
ins <- getInspectorS
a <- runS alloc
d <- bindS dealloc
u <- bindS use
pure $
X.bracketOnError
a
d
(\x -> do
result <- u x
case inspect ins result of
Just _ -> pure result
Nothing -> do
_ <- d x
pure result
)
{-# INLINE resourceToIOFinal #-}
------------------------------------------------------------------------------
-- | Run a 'Resource' effect purely.
--
-- @since 1.0.0.0
runResource
:: ∀ r a
. Sem (Resource ': r) a
-> Sem r a
runResource = interpretH $ \case
Bracket alloc dealloc use -> do
a <- runT alloc
d <- bindT dealloc
u <- bindT use
let run_it = raise . runResource
resource <- run_it a
result <- run_it $ u resource
_ <- run_it $ d resource
pure result
BracketOnError alloc dealloc use -> do
a <- runT alloc
d <- bindT dealloc
u <- bindT use
let run_it = raise . runResource
resource <- run_it a
result <- run_it $ u resource
ins <- getInspectorT
case inspect ins result of
Just _ -> pure result
Nothing -> do
_ <- run_it $ d resource
pure result
{-# INLINE runResource #-}