cleff-0.2.0.0: src/Cleff/Mask.hs
{-# LANGUAGE Trustworthy #-}
-- |
-- Copyright: (c) 2021 Xy Ren
-- License: BSD3
-- Maintainer: xy.r@outlook.com
-- Stability: experimental
-- Portability: non-portable (GHC only)
module Cleff.Mask
( -- * Effect
Mask (..)
, -- * Operations
mask, uninterruptibleMask, bracket, bracketOnError, mask_, uninterruptibleMask_, bracket_, finally, onError
, -- * Interpretations
runMask
) where
import Cleff
import Cleff.Internal.Base
import qualified UnliftIO.Exception as Exc
-- * Effect
-- | An effect capable of 'Exc.mask'ing and specifically, 'Exc.bracket'ing operations, /i.e./ allowing cleanup after
-- operations that my raise exceptions.
data Mask :: Effect where
Mask :: ((m ~> m) -> m a) -> Mask m a
UninterruptibleMask :: ((m ~> m) -> m a) -> Mask m a
Bracket :: m a -> (a -> m c) -> (a -> m b) -> Mask m b
BracketOnError :: m a -> (a -> m c) -> (a -> m b) -> Mask m b
-- * Operations
makeEffect ''Mask
-- | Variant of 'mask' that does not provide a restoring function.
mask_ :: Mask :> es => Eff es a -> Eff es a
mask_ m = mask \_ -> m
-- | Variant of 'uninterruptibleMask' that does not provide a restoring function.
uninterruptibleMask_ :: Mask :> es => Eff es a -> Eff es a
uninterruptibleMask_ m = uninterruptibleMask \_ -> m
-- | Variant of 'bracket' that does not pass the allocated resource to the cleanup action.
bracket_ :: Mask :> es => Eff es a -> Eff es c -> (a -> Eff es b) -> Eff es b
bracket_ ma = bracket ma . const
-- | Attach a cleanup action that will always run to a potentially throwing computation.
finally :: Mask :> es => Eff es a -> Eff es b -> Eff es a
finally m mz = bracket_ (pure ()) mz (const m)
-- | Attach an action that runs if the main computation throws an exception.
onError :: Mask :> es => Eff es a -> Eff es b -> Eff es a
onError m mz = bracketOnError (pure ()) (const mz) (const m)
-- * Interpretations
-- | Interpret the 'Mask' effect in terms of primitive 'IO' actions.
runMask :: Eff (Mask ': es) ~> Eff es
runMask = thisIsPureTrustMe . reinterpret \case
Mask f -> withToIO \toIO -> Exc.mask \restore -> toIO $ f (fromIO . restore . toIO)
UninterruptibleMask f -> withToIO \toIO -> Exc.uninterruptibleMask \restore -> toIO $ f (fromIO . restore . toIO)
Bracket ma mz m -> withToIO \toIO -> Exc.bracket (toIO ma) (toIO . mz) (toIO . m)
BracketOnError ma mz m -> withToIO \toIO -> Exc.bracketOnError (toIO ma) (toIO . mz) (toIO . m)
{-# INLINE runMask #-}