Polysemy: Tactics binding deprecated workaround
In a previous log, we ended with a solution for Resource which wasn't Error resistant.
The solution is to use the soo-deperecated withLowerToIO.
Let's start again with our simpler effect
data BindE (m :: Type -> Type) a where
BindE :: m a -> (a -> m b) -> BindE m b
makeSem ''BindE
We can express an impure (Embed-base) interpreter:
interpretBindTacticLowering =
interpretH $
\case
BindE f g -> do
ma <- runT f
mf <- bindT g
withLowerToIO $ \lower _ -> do
let toIO :: Sem (BindE ': r) x -> IO x
toIO = lower . raise . interpretBindTacticLowering
toIO ma >>= toIO . mf
withLowerToIO provides two functions (one to locally interpret a Sem r a to IO a and a finalizer) and expect a IO a, it's defined as:
withLowerToIO
:: Member (Embed IO) r
=> ((forall x. Sem r x -> IO x) -> IO () -> IO a)
-> Sem r a
Under the hood, a thread is created (the program should be compiled with -thread) to run the effect stack until IO, 'blocking' thread.
The finalizer does not need to be called, but it indicate the end of the new thread.
Let's see how it goes for Resource:
data Resource m a where
Bracket :: m a -> (a -> m c) -> (a -> m b) -> Resource m b
We are now able to use bracket:
resourceToIO =
interpretH $
\case
Bracket alloc dealloc use -> do
alloc' <- runS alloc
dealloc' <- bindS dealloc
use' <- bindS use
withLowerToIO $ \lower finish -> do
let runHoE :: Sem (Resource ': r) x -> IO x
runHoE = lower . raise . resourceToIO
Expection.bracket
(runHoE alloc')
(\x -> runHoE (use' x) >> finish)
(runHoE . dealloc')