polysemy-managed-0.1.0.0: src/Polysemy/Managed.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Polysemy.Managed
( Managed(..)
, runManagedResource
, runManaged
, runManagedFinal
, managedAsk
, managedLocal
) where
import Control.Exception qualified as E
import Control.Monad.Trans.Resource (createInternalState, MonadResource, InternalState)
import Control.Monad.Trans.Resource qualified as MTR
import Control.Monad.Trans.Resource.Internal qualified as RI
import Polysemy (Embed, Final, Sem, Member)
import Polysemy qualified as P
import Polysemy.Final qualified as PF
import Polysemy.Reader (Reader, runReader)
data Managed m a where
ManagedAsk :: Managed m InternalState
ManagedLocal :: m a -> Managed m a
P.makeSem ''Managed
runManaged :: forall a r. ()
=> Member (Embed IO) r
=> Sem (Managed ': r) a
-> Sem r a
runManaged f = do
state <- createInternalState
managedBracketImpl state $ runManagedImpl state f
{-# INLINE runManaged #-}
runManagedFinal :: forall a r. ()
=> Member (Final IO) r
=> Sem (Managed ': r) a
-> Sem r a
runManagedFinal f = do
state <- P.embedFinal @IO createInternalState
managedBracketFinalImpl state $ runManagedFinalImpl state f
{-# INLINE runManagedFinal #-}
runManagedResource :: ()
=> Member (Embed IO) r
=> Sem (Reader MTR.InternalState ': r) a
-> Sem r a
runManagedResource f = do
istate <- createInternalState
runReader istate f
--------------------------------------------------------------------------------
-- Internal
runManagedImpl :: forall a r. ()
=> Member (Embed IO) r
=> InternalState
-> Sem (Managed ': r) a
-> Sem r a
runManagedImpl state = P.interpretH $ \case
ManagedAsk -> P.pureT state
ManagedLocal m -> do
mm <- P.runT m
newState <- createInternalState
managedBracketImpl newState $ P.raise $ runManagedImpl newState mm
{-# INLINE runManagedImpl #-}
runManagedFinalImpl :: forall a r. ()
=> Member (Final IO) r
=> InternalState
-> Sem (Managed ': r) a
-> Sem r a
runManagedFinalImpl state = P.interpretH $ \case
ManagedAsk -> P.pureT state
ManagedLocal m -> do
mm <- P.runT m
newState <- P.embedFinal @IO createInternalState
managedBracketFinalImpl newState $ P.raise $ runManagedFinalImpl newState mm
{-# INLINE runManagedFinalImpl #-}
managedBracketImpl :: forall a r. ()
=> Member (Embed IO) r
=> MTR.InternalState
-> Sem r a
-> Sem r a
managedBracketImpl istate f = do
P.withLowerToIO $ \lower finish -> do
E.mask $ \restore -> do
res <- restore (lower f) `E.catch` \e -> do
RI.stateCleanupChecked (Just e) istate
E.throwIO e
RI.stateCleanupChecked Nothing istate
finish
return res
managedBracketFinalImpl :: forall a r. ()
=> Member (Final IO) r
=> MTR.InternalState
-> Sem r a
-> Sem r a
managedBracketFinalImpl istate f = PF.withStrategicToFinal @IO $ do
f' <- PF.runS f
pure $ E.mask $ \restore -> do
res <- restore f' `E.catch` \e -> do
RI.stateCleanupChecked (Just e) istate
E.throwIO e
RI.stateCleanupChecked Nothing istate
pure res
instance
( Member (Embed IO) r
, Member Managed r
) => MonadResource (Sem r) where
liftResourceT (RI.ResourceT r) = managedAsk >>= P.embed . r