Polysemy: Scoped
Gautier DI FOLCO May 28, 2023 [Haskell] #haskell #polysemy #design #effects systemsWith Polysemy 1.9.0.0 came the Scoped
effect.
It's defined as follows:
data Scoped (param :: Type) (effect :: Effect) :: Effect where
Run :: forall param effect m a. Word -> effect m a -> Scoped param effect m a
InScope :: forall param effect m a. param -> (Word -> m a) -> Scoped param effect m a
Complex, the interesting function is scoped
:
Lets imagine a simple Queue
effect:
data Queue q (m :: Type -> Type) a where
Dequeue :: Queue q m (Maybe q)
makeSem ''Queue
we can use it as follows:
=
scoped @_ @(Queue ()) (Just ()) $ do
void $ dequeue @()
void $ dequeue @()
fails =
scoped @_ @(Queue ()) (Just ()) $ do
void $ dequeue @()
() <- throw ()
void $ dequeue @()
works
We define a 1-element Queue
.
We can then produce an interpreter allocating a MVar
= do
mvar <- embed $ maybe newEmptyMVar newMVar initial
use mvar
interpretQueue =
interpretScopedWith @'[] withQueue $ \mvar -> \case
Dequeue -> do
embed $ putStrLn "deqeue"
embed $ tryTakeMVar mvar
withQueue initial use
Usable as follows:
putStrLn "Work"
runM (runError @() $ interpretQueue @() works) >>= print
putStrLn "Fail"
runM (runError @() $ interpretQueue @() fails) >>= print
Giving:
Work
deqeue
deqeue
Right ()
Fail
deqeue
Left ()
You can notice @[]
in:
interpretScopedWith @'[] withQueue $ \mvar -> \case
it's actually a way to inject effects during interpretation:
= do
mvar <- embed $ maybe newEmptyMVar newMVar initial
runStackMVar mvar $ use ()
runStackMVar mvar =
interpret $
\case
Pop -> embed $ tryTakeMVar mvar
interpretQueue' =
interpretScopedWith @'[Stack q] withQueue' $ \() -> \case
Dequeue -> do
embed $ putStrLn "deqeue"
pop
withQueue' initial use
See the full the code here.