A software engineer website

Polysemy: Strategies binding

Gautier DI FOLCO January 22, 2023 [Haskell] #haskell #polysemy #design #effects systems

In a previous log, we introduced liftS and runS which helps running higher-order effects in the interpretation Monad.

However, you sometime have effects with multiple higher-order effects needing to be bound, sur as Resource:

data Resource m a where
  Bracket :: m a -> (a -> m c) -> (a -> m b) -> Resource m b

In order to have a working interpreter we have to use bindS:

resourceToIOFinal :: Member (Final IO) r => InterpreterFor Resource r
resourceToIOFinal =
  interpretFinal @IO $
    \case
      Bracket alloc dealloc use -> do
        alloc' <- runS alloc
        dealloc' <- bindS dealloc
        use' <- bindS use
        return $ Exception.bracket alloc' dealloc' use'

bindS is pretty straightforward:

bindS :: (a -> n b) -> Sem (WithStrategy m f n) (f a -> m (f b))

With a simpler effect:

data BindE (m :: Type -> Type) a where
  BindE :: m a -> (a -> m b) -> BindE m b

makeSem ''BindE

We see that a simple bind does the trick:

interpretBindFinal :: Member (Final IO) r => InterpreterFor BindE r
interpretBindFinal =
  interpretFinal @IO $
    \case
      BindE f g -> do
        fa <- runS f
        ff <- bindS g
        pure $ fa >>= ff

See the full the code here and here.