I managed to convert between `P.Producer (ZipList a) m r` and `ZipList (P.Producer a m ()` using the StateT trick used in Pipes.Parse
import Control.Applicative
import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import qualified Pipes as P
import qualified Pipes.Lift as PL
-- ZipList Traversable is only in 4.9.0.0
sequenceAZipList :: Applicative f => ZipList (f a) -> f (ZipList a)
sequenceAZipList xs = ZipList <$> sequenceA (getZipList xs)
-- | Similar to Pipes.Parse.Parser, except it stores a ZipList of Producers.
type ZipParser a m r = forall x . StateT (ZipList (P.Producer a m x)) m r
-- | Draw one element from each underlying Producer, returning 'Nothing' if any of the producers are empty
drawZ :: Monad m => ZipParser a m (Maybe (ZipList a))
drawZ = do
ps <- get
rs <- lift (sequenceAZipList (P.next <$> ps))
case sequenceAZipList rs of
Left _ -> pure Nothing
Right rs' -> do
put $ snd <$> rs'
pure . Just $ fst <$> rs'
-- | Push back a Ziplist element onto the underlying ZipList of Producers
unDrawZ :: Monad m => ZipList a -> ZipParser a m ()
unDrawZ as = modify (\ps -> appendA <$> ps <*> as)
where
appendA p a = do
r <- p
P.yield a
pure r
toZipList :: Monad m => P.Producer (ZipList a) m r -> m (ZipList (P.Producer a m ()))
toZipList p = execStateT (toZipParser p) (pure (pure ()))
where
toZipParser :: Monad m => P.Producer (ZipList a) m r -> ZipParser a m r
toZipParser p' = do
r <- lift $ P.next p'