Lifting a polysemy effect to m - General

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Edward Yang

My application depends on a couple libraries where the run off of IO, rather than a more generic m.
The code is littered with runFinal @IO . embedToFinal @IO . otherInterpreters, etc.
In order to circumvent this, I thought I could simple pass in some sort of "hoisting" function as a parameter.

For example:

foo :: Members [Embed IO, Async] r => (forall m a. m a -> IO a) -> Sem r a -> IO a
foo hoist sem = hoist sem

The above compiles, but the problem is passing in the "hoisting" function.

main :: IO ()
main = do
  let hoist = runFinal @IO . embedToFinal @IO . asyncToIO
  foo hoist undefined
  return ()

The compiler complains when passing hoist to the function foo due to the type variable r being ambiguous.
Is there a way to "lift" the Sem effect to m to satisfy this constraint? Or perhaps a better way of doing what I'm trying to achieve?

Torsten Schmits

the usual way to achieve this would be to use interpretFinal or withWeavingToFinal. those provide lifting combinators

Edward Yang

I'll look into how to use those. Thanks for pointing this out!

Edward Yang

Sorry, do you mind showing an example actually? I'm not good at reading polysemy's type signatures at all :sweat:

Torsten Schmits

sure :smile: it depends heavily on what kind of combinators your IO library provides. if it's just a simple case of calling a function that expects an IO as its argument, it could look like this:

interpretRace ::
  Member (Final IO) r =>
  InterpreterFor Race r
interpretRace =
  interpretFinal @IO \case
    Race.Race left right ->
      fmap (fmap biseqEither) . Async.race <$> runS left <*> runS right

(ignore the biseqEither)

here race is IO a -> IO b -> IO (Either a b). interpretFinal expects the result of the handler to be Sem '[Strategy] x, and the runS combinator turns a Sem r x into a Sem '[Strategy] (IO (f x)).
from there you just have to make the types align (which isn't trivial either :wink: )

Torsten Schmits

withWeavingToFinal is a bit more low-level, and it can be used without interpreting an effect

Torsten Schmits

(also possible with withStrategicToFinal)

Edward Yang

I see, thanks a lot for the explanation! I appreciate it

Torsten Schmits

if you'll still get stuck you can post some code and we'll figure it out :smile:

Edward Yang

Going to slowly try and break down your explanation so I can fully understand that first.
If I'm still stuck after that, I'll let you know. I really do appreciate the help, thank you :grinning:

Edit: I understand the example a lot more now :grinning:

Torsten Schmits

did you have success using it?