4

In the code below how can I

  • change stdoutCharConsumer so that it prints a newline after printing all the data from the input stream

  • implement mixin and mixin'

without going into Pipes.Internal? Is it possible? I need someting like the next function for Producers.

I use Pipes 4.1.0

#!/usr/bin/env runhaskell
{-# OPTIONS_GHC -Wall #-}

import Pipes

digits, characters :: Monad m => Producer Char m ()
digits = each "0123456789"
characters = each "abcdefghijklmnopqrstuvwxyz"

interleave :: Monad m => Producer a m () -> Producer a m () -> Producer a m ()
interleave a b = do
  n <- lift $ next a
  case n of
    Left () -> b
    Right (x, a') -> do
      yield x
      interleave b a'

stdoutCharConsumer :: Consumer Char IO ()
stdoutCharConsumer = await >>= liftIO . putChar >> stdoutCharConsumer

-- first element of the mixin should go first
mixin :: Monad m => Producer b m () -> Pipe a b m ()
mixin = undefined

-- first element of the pipe should go first
mixin' :: Monad m => Producer b m () -> Pipe a b m ()
mixin' = undefined

main :: IO ()
main = do

    -- this prints "a0b1c2d3e4f5g6h7i8j9klmnopqrstuvwxyz"
    runEffect $ interleave characters digits >-> stdoutCharConsumer
    putStrLn ""

    -- this prints "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ interleave digits characters >-> stdoutCharConsumer
    putStrLn ""

    -- should print "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ characters >-> mixin digits >-> stdoutCharConsumer
    putStrLn ""

    -- should print "a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ digits >-> mixin characters >-> stdoutCharConsumer
    putStrLn ""

    -- should print "a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ characters >-> mixin' digits >-> stdoutCharConsumer
    putStrLn ""

    -- should print "0a1b2c3d4e5f6g7h8i9jklmnopqrstuvwxyz"
    runEffect $ digits >-> mixin' characters >-> stdoutCharConsumer
    putStrLn ""

UPD: Now after I read about pull/pushs based streams I think it is impossible even with Pipes.Internal. Is it true?

amakarov
  • 524
  • 3
  • 16
  • Do you understand how `interleave` works? Notice that `next p` returns `Left result` when the pipe `p` is done. – pash Mar 15 '14 at 21:37
  • I think I understand it, but it works with Producers, not Pipes. That's why I left `interleave` here as an example of what I have for Producers and want to have for Pipes – amakarov Mar 16 '14 at 02:23
  • Sorry, I thought `next` had a more general type. If you do dive into `Pipes.Internal`, look at the `MonadPlus` instance for inspiration. Possibly you can tweak the idea behind the implementation of `mplus` to get something similar to `interleave` for more general `Proxy` values. – pash Mar 16 '14 at 22:12

1 Answers1

3

Neither Consumers nor Pipes are aware of upstream end-of-input. You need Parsers from pipes-parse for that.

Compared to Consumers, Parsers a more direct knowledge of Producers; their draw function (roughly analogous to await) returns a Nothing when end-of-input is found.

import qualified Pipes.Parse as P

stdoutCharParser :: P.Parser Char IO ()
stdoutCharParser = P.draw >>= \ma ->
    case ma of 
        Nothing -> liftIO (putStrLn "\n")
        Just c -> liftIO (putChar c) >> stdoutCharParser

To run a parser, we invoke evalStateT instead of runEffect:

P.evalStateT stdoutCharParser (interleave characters digits) 

As for mixin and mixin', I suspect they will be impossible to write to work as intended. The reason is that the result Pipe would have to be aware of upstream termination in order to know when to yield the remaining values of the Producer passed as parameter.

danidiaz
  • 26,936
  • 4
  • 45
  • 95
  • Probably you are right. I would explain it this way. Pipes do not decide if the stream is closed, instead they return control upstream. When the upstream producer decides it is exhausted it just terminates without letting know the pipes/cousumer below it. – amakarov Mar 16 '14 at 04:15
  • @amakarov That's correct. `Parsers` from `pipes-parse` cede less control to the `Producer`. They can detect end-of-input and also push back values onto the `Producer`. – danidiaz Mar 16 '14 at 07:49