Bidirectional pipe to interact with a process

123 views
Skip to first unread message

Andrea Corradi

unread,
Oct 7, 2015, 5:05:48 PM10/7/15
to haskel...@googlegroups.com
Hi all,

I want to interact with a process through its stdin/stdout;
the interaction is similar to the one with a server where
I send request via stdin and I get the response via stdout.

For this reason I was trying to use a producer and a consumer
to make a Server and compose it with a Client using +>>

Function that should make a Server:

serverProcess toPipe fromPipe input = do
(yield input >~ return ExitSuccess) >-> toPipe
r <- next fromPipe
case r of
Left e -> return e
Right (output,fromPipe') ->
do input' <- respond output
serverProcess toPipe fromPipe' input'

This function has type
serverProcess :: Monad m =>
Proxy () b1 b1 c m ExitCode
-> Producer c (Proxy a' a b1 c m) b -> b1 -> Proxy a' a b1 c m b

and is not usable as I wanted. Unfortunately I could not come up with
a solution.

I'm using pipes-cliff to open the process and get the Producer
and Consumer pipes.


Is there a way to make this work?
Is there a more idiomatic way to do this?

Thanks,
Andrea

Daniel Díaz

unread,
Oct 8, 2015, 8:08:55 PM10/8/15
to Haskell Pipes
> I send request via stdin and I get the response via stdout. 

When reading stdout, what should count as a "complete response", so that the next request can be emitted to stdin? Is every response is just a single line of text, or can they be multiline?

Andrea Corradi

unread,
Oct 9, 2015, 4:53:30 PM10/9/15
to Haskell Pipes


On Friday, October 9, 2015 at 2:08:55 AM UTC+2, Daniel Díaz wrote:
> I send request via stdin and I get the response via stdout. 

When reading stdout, what should count as a "complete response", so that the next request can be emitted to stdin? Is every response is just a single line of text, or can they be multiline?

Its a single line text. 

Gabriel Gonzalez

unread,
Oct 11, 2015, 4:09:30 PM10/11/15
to haskel...@googlegroups.com, and...@unstable.it
I just wanted to point out that your code probably does not do what you
think it does.

This pipeline:

(yield input >~ return ExitSuccess) >-> toPipe

... is equivalent to:

return ExitSuccess

The `yield input` and `toPipe` never get used.

If you can explain what you were trying to do I can show you how to
encode your desired behavior.

Andrea Corradi

unread,
Oct 12, 2015, 9:49:44 AM10/12/15
to Haskell Pipes, and...@unstable.it
I want to interact with a process using its stdin/stdout like an interactive shell.

I do not understand how to organize the pipeline since its not a straightforward pipeline from
the Producer to the Consumer  but I have to send data to the consumer and then wait the reply from
the Producer and send another command to the Consumer and so on.

In this respect I was trying to 'fuse' the Produce and Consumer pipes of the process in a Server
and interact with it with a Client.

Andrea

Andrea Corradi

unread,
Oct 14, 2015, 6:58:00 PM10/14/15
to Haskell Pipes, and...@unstable.it
I wrote an example using directly the handlers, I would like to use Pipe.Cliff that manage the lower part for me 
and use directly pipes instead of the Handle.
In this example the client send 'command' to cat and output the response.

Does this seams reasonable?

-- CODE --
{-# LANGUAGE OverloadedStrings #-}
import Pipes
import Pipes.Core
import Control.Monad (unless)
import System.Process
import qualified System.IO as IO

createServer :: MonadIO m => IO.Handle -> IO.Handle -> String -> Server String String m ()
createServer hin hout input = do
  liftIO $ IO.hPutStrLn hin input
  eof <- liftIO $ IO.hIsEOF hout
  unless eof $ do
    output <- liftIO $ IO.hGetLine hout
    input' <- respond output
    createServer hin hout input'

client :: MonadIO m => Client String String m ()
client = do
  reply <- request "Hello"
  liftIO $ IO.putStrLn $ "The reply is: " ++ reply
  reply <- request "World"
  liftIO $ IO.putStrLn $ "The reply is: " ++ reply

main = do
  (Just hin, Just hout, _, ph) <-
    createProcess (proc "cat" []){ std_in = CreatePipe, std_out = CreatePipe }
  IO.hSetBuffering hin IO.NoBuffering
  runEffect $ (createServer hin hout) +>> client
  IO.hClose hin
  System.Process.waitForProcess ph

Daniel Díaz

unread,
Oct 14, 2015, 7:08:38 PM10/14/15
to Haskell Pipes, and...@unstable.it
Here is my attempt:


I didn't manage to put everything in a single pipeline... I used two independent pipelines that communicate using an MVar.

Andrea Corradi

unread,
Oct 18, 2015, 3:52:25 PM10/18/15
to Haskell Pipes, and...@unstable.it
I noticed that our two examples do work with cat but do not work for example with tr or other programs.

Here: https://gist.github.com/anonymous/b5da2b00ed2993594577
I simplified Daniel approach removing the server.
This code print "await" and then terminate with:
thread blocked indefinitely in an STM transaction

If you comment out the takeMVar it work properly.

The example using pipes-cliff do not print anything, just hang.

I think that yield does not immediately send the string to the pipe and process wait forever 
on takeMVar.

Is possible to fix this?

Andrea




Daniel Díaz

unread,
Oct 23, 2015, 11:48:24 AM10/23/15
to Haskell Pipes, and...@unstable.it
> I noticed that our two examples do work with cat but do not work for example with tr or other programs.

I suspect the cause is that tr buffers its output, and doesn't emit its lines "right away", unlike cat.
Reply all
Reply to author
Forward
0 new messages