Fusing conduits with multiple inputs

后端 未结 2 1358
闹比i
闹比i 2021-01-02 09:23

I am trying to create a conduit that can consume multiple input streams. I need to be able to await on one or the other of the input streams in no particular order (e.g., no

2条回答
  •  轮回少年
    2021-01-02 09:58

    This can be done by diving into the internals of conduit. I wanted to avoid this because it looked extremely messy. Based on the responses here, it sounds like there is no way around it (but I would really appreciate a cleaner solution).

    The key difficulty is that (x =$=) is a pure function, but to make transPipe give the correct answer, it needs a kind of stateful, function-like thing:

    data StatefulMorph m n = StatefulMorph
        { stepStatefulMorph :: forall a. m a -> n (StatefulMorph m n, a)
        , finalizeStatefulMorph :: n () }
    

    Stepping StatefulMorph m n takes a value in m and returns, in n, both that value and the next StatefulMorph, which should be used to transform the next m value. The last StatefulMorph should be finalized (which, in the case of the "stateful (x =$=)", finalizes the x conduit.

    Conduit fusion can be implemented as a StatefulMorph, using the code for pipeL with minor changes. The signature is:

    fuseStateful :: Monad m
                 => Conduit a m b
                 -> StatefulMorph (ConduitM b c m) (ConduitM a c m)
    

    I also need a replacement for transPipe (a special case of hoist) that uses StatefulMorph values instead of functions.

    class StatefulHoist t where
        statefulHoist :: (Monad m, Monad n)
                      => StatefulMorph m n
                      -> t m r -> t n r
    

    A StatefulHoist instance for ConduitM i o can be written using the code for transPipe with some minor changes.

    fuseInner is then easy to implement.

    fuseInner :: Monad m
              => Conduit a m b
              -> ConduitM i o (ConduitM b c m) r
              -> ConduitM i o (ConduitM a c m) r
    fuseInner left = statefulHoist (fuseStateful left)
    

    I've written a more detailed explanation here and posted the full code here. If someone can come up with a cleaner solution, or one that uses the conduit public API, please post it.

    Thanks for all the suggestions and input!

提交回复
热议问题