conduit icon indicating copy to clipboard operation
conduit copied to clipboard

Why was this done to MonadResource?

Open ghost opened this issue 5 years ago • 3 comments

Here is example of people talking. MonadResource did something useful for them, but since the unliftio change, MonadResource became useless. Why was a popular "standard" type class in the Haskell community damaged like this?

What can be done, if anything?

ghost avatar Feb 07 '20 21:02 ghost

This is where they rely on MonadBaseControl. They're using it "for saving and restoring the state of monad when we fork a concurrent thread". Here's the code where they use it. Can they achieve their aims without using MonadBaseControl?

------------------------------------------------------------------------------
-- Spawning threads
------------------------------------------------------------------------------

-- | A monad that can perform concurrent or parallel IO operations. Streams
-- that can be composed concurrently require the underlying monad to be
-- 'MonadAsync'.
--
-- @since 0.1.0
type MonadAsync m = (MonadIO m, MonadBaseControl IO m, MonadThrow m)

-- When we run computations concurrently, we completely isolate the state of
-- the concurrent computations from the parent computation.  The invariant is
-- that we should never be running two concurrent computations in the same
-- thread without using the runInIO function.  Also, we should never be running
-- a concurrent computation in the parent thread, otherwise it may affect the
-- state of the parent which is against the defined semantics of concurrent
-- execution.
newtype RunInIO m = RunInIO { runInIO :: forall b. m b -> IO (StM m b) }

captureMonadState :: MonadBaseControl IO m => m (RunInIO m)
captureMonadState = control $ \run -> run (return $ RunInIO run)

-- Stolen from the async package. The perf improvement is modest, 2% on a
-- thread heavy benchmark (parallel composition using noop computations).
-- A version of forkIO that does not include the outer exception
-- handler: saves a bit of time when we will be installing our own
-- exception handler.
{-# INLINE rawForkIO #-}
rawForkIO :: IO () -> IO ThreadId
rawForkIO action = IO $ \ s ->
   case fork# action s of (# s1, tid #) -> (# s1, ThreadId tid #)

{-# INLINE doFork #-}
doFork :: MonadBaseControl IO m
    => m ()
    -> RunInIO m
    -> (SomeException -> IO ())
    -> m ThreadId
doFork action (RunInIO mrun) exHandler =
    control $ \run ->
        mask $ \restore -> do
                tid <- rawForkIO $ catch (restore $ void $ mrun action)
                                         exHandler
                run (return tid)

Code BSD3- licensed, taken from here: https://github.com/composewell/streamly/blob/v0.7.0/src/Streamly/Internal/Data/SVar.hs#L888

ghost avatar Feb 08 '20 08:02 ghost

You shouldn't rely on MonadBaseControl. Here are some resources:

https://www.fpcomplete.com/blog/2017/07/announcing-new-unliftio-library

https://old.reddit.com/r/haskell/comments/6nr9ya/announcing_the_new_unliftio_library/

https://www.snoyman.com/blog/2018/02/conduitpocalypse

bitemyapp avatar Feb 08 '20 16:02 bitemyapp

@bitemyapp Unfortunately, I'm not sure we can do without it. If we can, great. If not, we'd have proof that resourcet can no longer do things it previously could. I don't have enough expertise to establish which.

ghost avatar Feb 19 '20 19:02 ghost