Thanks! I just read your article. I think your proposal is rational, useful and so brilliant! The new yield/await style would make writing conduits much easier.
Thank you again for taking so much time for this problem! On 2012/05/29, at 22:14, Michael Snoyman wrote: > OK, after thinking on this for the past week, I've come up with a > proposal to make this kind of code easier to write (and more of an > explanation on why the behavior was unintuitive in the first place). > > http://www.yesodweb.com/blog/2012/05/next-conduit-changes > > Do you think the modified yield/await would be a good solution to the problem? > > Michael > > On Mon, May 21, 2012 at 6:07 AM, Michael Snoyman <mich...@snoyman.com> wrote: >> I agree that this behavior is non-intuitive, but still believe it's >> the necessary approach. The short answer to why it's happening is that >> there's no exit path in the yield version of the function. To >> understand why, let's expand the code a little bit. Realizing that >> >> liftIO = lift . liftIO >> >> and >> >> lift mr = PipeM (Done Nothing `liftM` mr) (Finalize mr) >> >> we can expand the yield version into: >> >> sourceTChanYield2 ch = forever $ do >> let action = liftIO . atomically $ readTChan ch >> ans <- PipeM (Done Nothing `liftM` action) (FinalizeM action) >> yield ans >> >> So the first hint that something is wrong is that the finalize >> function is calling the action. If you try to change that finalize >> action into a no-op, e.g.: >> >> sourceTChanYield3 :: MonadIO m => TChan a -> Source m a >> sourceTChanYield3 ch = forever $ do >> let action = liftIO . atomically $ readTChan ch >> ans <- PipeM (Done Nothing `liftM` action) (return ()) >> yield ans >> >> then you get an error message: >> >> test.hs:36:53: >> Could not deduce (a ~ ()) >> >> The problem is that, as the monadic binding is set up here, the code >> says "after running the PipeM, I want you to continue by yielding, and >> then start over again." If you want to expand it further, you can >> change `forever` into a recursive call, expand `yield`, and then >> expand all the monadic binding. Every finalization call is forcing >> things to keep running. >> >> And remember: all of this is the desired behavior of conduit, since we >> want to guarantee finalizers are always called. Imagine that, instead >> of reading data from a TChan, you were reading from a Handle. In the >> code above, there was no way to call out to the finalizers. >> >> Not sure if all of that rambling was coherent, but here's my >> recommended solution. What we need is a helper function that allows >> you to branch based on whether or not it's time to clean up. `lift`, >> `liftIO`, and monadic bind all perform the same actions regardless of >> whether or not finalization is being called. The following code, >> however, works correctly: >> >> liftFinal :: Monad m => m a -> Finalize m () -> (a -> Source m a) -> Source >> m a >> liftFinal action final f = PipeM (liftM f action) final >> >> sourceTChanYield :: Show a => MonadIO m => TChan a -> Source m a >> sourceTChanYield ch = liftFinal >> (liftIO . atomically $ readTChan ch) >> (return ()) >> $ \ans -> do >> yield ans >> sourceTChanYield ch >> >> Michael >> >> On Sun, May 20, 2012 at 4:22 PM, Hiromi ISHII <konn.ji...@gmail.com> wrote: >>> Oops, sorry. >>> The last case's behaviour was not as I expected... A correct log is below: >>> >>> ~~~~ >>> ghci> sourceTChanRaw ch $$ LC.isolate 10 =$= LC.mapM_ print >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> ghci> sourceTChanState ch $$ LC.isolate 10 =$= LC.mapM_ print >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> ghci> sourceTChanYield ch $$ LC.isolate 10 =$= LC.mapM_ print >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> () >>> *blocks* >>> ~~~~ >>> >>> So again, sourceTChanYield blocks here even if it is already supplied with >>> enough values! >>> >>> -- Hiromi ISHII >>> konn.ji...@gmail.com >>> >>> >>> >>> >>> _______________________________________________ >>> Haskell-Cafe mailing list >>> Haskell-Cafe@haskell.org >>> http://www.haskell.org/mailman/listinfo/haskell-cafe -- Hiromi ISHII konn.ji...@gmail.com _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe