Gabriella439 / pipes

Compositional pipelines
BSD 3-Clause "New" or "Revised" License
487 stars 72 forks source link

implement runPartial. Run monadic actions and extract a return value if ... #135

Open alang9 opened 9 years ago

alang9 commented 9 years ago

...possible

Seems to be a useful utility function that's currently missing.

Gabriella439 commented 9 years ago

Can you give an example of how you planned to use this?

alang9 commented 9 years ago

My particular application is something like this:

consumerToIteratee :: (Monad m, I.Nullable a) => Consumer a m r -> I.Iteratee a m r
consumerToIteratee = go
  where
    go consumer = do
      a <- I.getChunk
      out <- lift $ runPartial $ return a >~ consumer
      case out of
        Left r -> return r
        Right next -> go next

where I.Iteratee is from the iteratee package.

Gabriella439 commented 9 years ago

In this case you can use runEffect instead of runPartial. return a >~ consumerwill type-check as an Effect, meaning that it's guaranteed not to have any Request/Respond constructors.

You can also tell that it won't have any Request/Respond constructors by observing that Consumer only has Request/Pure/M constructors, and then (return a >~) eliminates all the Request constructors.

alang9 commented 9 years ago

I see. I think >~ doesn't do what I thought it did. Upon further inspection, I agree that runPartial is not very useful, but I would like a function like feed

feed :: Monad m => a -> Consumer a m r -> m (Either r (Consumer a m r))
feed a = go
  where
    go c = case c of
      Request () fu -> go2 (fu a)
      Respond v _ -> closed v
      M m -> m >>= go
      Pure r -> return (Left r)
    go2 c = case c of
      Request _ _ -> return (Right c)
      Respond v _ -> closed v
      M m -> m >>= go2
      Pure r -> return (Left r)

To be able to write:

consumerToIteratee :: (Monad m, I.Nullable a) => Consumer a m r -> I.Iteratee a m r
consumerToIteratee = go
  where
    go consumer = do
      a <- I.getChunk
      out <- lift $ feed a consumer
      case out of
        Left r -> return r
        Right next -> go next

Alternately, feed could be the following, in which case it looks dual to next:

feed2 :: Monad m => a -> Consumer a m r -> m (Either r (a -> Consumer a m r))
feed2 a = go
  where
    go c = case c of
      Request () fu -> go2 (fu a)
      Respond v _ -> closed v
      M m -> m >>= go
      Pure r -> return (Left r)
    go2 c = case c of
      Request () fu -> return (Right fu)
      Respond v _ -> closed v
      M m -> m >>= go2
      Pure r -> return (Left r)
Gabriella439 commented 9 years ago

I like the idea of some feed-like function that lets you step Consumer. Could I suggest this slight variation on the type signature you gave

feed :: Monad m => Consumer a m r -> m (Either r (a -> Consumer a m r))

Notice how you don't supply the a directly to feed. Instead, it may give you a (a -> Consumer a m r) and you supply the a to that instead.