Chris Smith
unread,Jun 7, 2012, 2:23:39 PM6/7/12Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to streamin...@googlegroups.com
We've had a few conversations on Reddit and here about exposing too
much implementation detail, about the resulting observable differences
that seem to violate monad transformer laws, and the like. So I spent
some time thinking about that issue. Just exposing await (or
tryAwait) and yield primitives is clearly not powerful enough, as
simple things like:
rightP :: Pipe a b m r -> Pipe (Either c a) (Either c b) m r
cannot be defined in those terms. One must fall back to pattern
matching on the constructors. Upon further thought, I realized that
the root of the problem is that runPipe is too limited. So I wrote
this... here I'm using the 5-parameter pipes without finalization to
keep things simple, but the same idea applies to others as well, I
believe.
simulatePipe :: (Monad m, MonadTrans t, Monad (t m))
=> t m (Either r a)
-> (b -> t m ())
-> Pipe a b r m s
-> t m s
simulatePipe up down (Yield p x) = down x >> simulatePipe up down p
simulatePipe up down (Await f) = simulatePipe up down . f =<< up
simulatePipe up down (Do m) = lift m >>= simulatePipe up down
simulatePipe up down (Done x) = return x
runPipe :: Monad m => Pipe () Void r m s -> m s
runPipe = runIdentityT . simulatePipe (return (Right ()))
(error "runPipe: impossible
yield of Void")
And at that point, one can define, for example:
leftP :: Monad m => Pipe a b r m s -> Pipe (Either a c) (Either b c) r m s
leftP p = simulatePipe up down p
where up = do x <- tryAwait
case x of Left r -> return (Left r)
Right (Left x) -> return (Right x)
Right (Right x) -> yield (Right x) >> up
down = yield . Left
Because simulatePipe requires a MonadTrans instance that comes with
certain laws, this dodges the complaint about (lift . return /=
return) that has come up in the past, and it lets you nicely run an
incomplete pipeline . Yet it should let you build pretty much any
desired pipe. This seems like an improvement to me.
Comments?