haskell-effectful/effectful

How to interoperate with concurrent monad transformers

sullyj3 opened this issue · 6 comments

I'm trying to use effectful together with streamly. I have a SerialT (Eff es) a, which uses streamly's concurrency combinators internally (async and fromAsync). I'm getting the runtime error:

If you want to use the unlifting function to run Eff computations in multiple threads, have a look at UnliftStrategy (ConcUnlift).

I had a look at the docs for ConcUnlift, but I'm struggling to understand how and where to use it. I had hoped I could just use it at the the entry point to my program, and maybe it would work but just be a little slower than if it was targeted more specifically:

-- (simplified)
main :: IO ()
main = do
  config <- getConfig
  runEff .
    withUnliftStrategy (ConcUnlift Ephemeral Unlimited) .
    runFailIO .
    runReader config $
    myProgram

But it doesn't seem to solve the problem - it still results in the same runtime error. Would it be possible to get some relevant examples in the documentation?

It's a bit unclear without full example, but if you look at the documentation of withEffToIO, it says that the strategy is reset to SeqUnlift inside a continuation (that's the same function that MonadUnliftIO and MonadBaseControl instances use).

Though you're the second person that finds it surprising, so perhaps it shouldn't do that.

Can you check if #70 fixes the problem?

I'm pretty sure not resetting the strategy is the correct choice either way, because if it's reset, then it's impossible to re-use external pieces of code that use unlifting and spawn more than one level of threads.

For some reason I didn't think of this scenario before 😞

Thanks for the rapid response! Works perfectly, thank you! Although I don't really understand why, since I'm not calling withEffToIO. Is that happening under the hood somewhere? I think I'm just generally confused about where and why unlifting is occurring.

Is that happening under the hood somewhere?

Yes, instances of MonadUnliftIO / MonadBaseControl IO use this function.

Ah, that makes sense.