Bluefin streams finalize promptly

Article: Bluefin streams finalize promptly

Despite the long struggle to make streaming abstractions finalize (release resources) promptly (for example, by implementing special-purpose bracketing operations using ResourceT for conduit and SafeT for pipes), they still fail in this task. At best, they have “promptish” finalization.

Streams implemented in Bluefin, on the other hand, do finalize promptly and can even use general-purpose bracketing!

My article explains the situation.

26 Likes

Anything that can tame the hornet’s nest of exceptions and resource-safety is very welcome indeed. Impressive work!

6 Likes

What happens if the consumer throws instead of the producer? Will the producer still promptly finalize?

An important question! The answer is yes:

bluefin3 :: IO ()
bluefin3 = runEff $ \io -> do
  connectCoroutines (consume io) (produce io)
  connectCoroutines (consume io) (produce io)
  where
    produce ::
      (e1 :> es, e2 :> es) =>
      IOE e1 ->
      () ->
      Stream Int e2 ->
      Eff es ()
    produce io () y = do
        -- General-purpose Bluefin bracket
        B.bracket
          (effIO io (putStrLn "Acquiring resource"))
          (\_ -> effIO io (putStrLn "Releasing resource"))
          ( \_ -> for_ [1 :: Int .. 3] $ \i -> do
              B.yield y i
          )

    consume ::
      (e1 :> es, e2 :> es) =>
      IOE e1 ->
      Coroutine () Int e2 ->
      Eff es ()
    consume io c = forever $ do
      -- General-purpose Bluefin try
      void $ B.try $ \e -> do
        r <- yieldCoroutine c ()
        when (r >= 3) (B.throw e ())
        effIO io (print r)
ghci> bluefin3
Acquiring resource
1
2
Releasing resource
Acquiring resource
1
2
Releasing resource
5 Likes