admin管理员组文章数量:1290959
The problem
I have the following code
--filename main.hs
main :: IO ()
main = do
loop "hello" "bye"
loop :: String -> String -> IO ()
loop inp1 inp2 = do
putStrLn $ "Type " ++ inp1
userInput <- getLine
if userInput == inp1
then do
putStrLn "correct answer"
loop inp2 inp1
else if userInput == "exit"
then do
putStrLn "exiting program"
return ()
else do
loop inp1 inp2
After compiling it, I can run it in the terminal and type commands such as hello bye and exit. The program works.
I made a second script to interact the with the first script
--filename test_program.hs
import System.IO (hPutStrLn, hFlush, hGetLine, hClose, hSetBuffering, BufferMode( NoBuffering,LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
proc,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
hSetBuffering hout NoBuffering
putStrLn =<< hGetLine hout
hPutStrLn hin "bla"
putStrLn =<< hGetLine hout
hPutStrLn hin "hello"
putStrLn =<< hGetLine hout
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
When I run this program I expect to the following sequence of messages
- type hello
- type hello
- correct answer
- type bye
- exiting program
but nothing happens. If I rearrange the code in the following manner
--filename test_program.hs
import System.IO (hPutStrLn, hFlush, hGetLine, hClose, hSetBuffering, BufferMode( NoBuffering,LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
proc,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
hSetBuffering hout NoBuffering
hPutStrLn hin "bla"
hPutStrLn hin "hello"
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
where I place all the stdin in commands in the beginning then read out the stdout, I get to see the expected sequence of messages. My question is why does it work for one case but not the other case? In my mind, if I write all the arguments first then read the outputs, or write then read should not affect the behavior of the program.
Edit 1
Thanks to suggestion by Daniel Wagner my scripts are working as expected. I have written the scripts with the fixes below
--filename main.hs
import System.IO (hSetBuffering,
stdout,
BufferMode(LineBuffering))
main :: IO ()
main = do
hSetBuffering stdout LineBuffering
loop "hello" "bye"
loop :: String -> String -> IO ()
loop inp1 inp2 = do
putStrLn $ "Type " ++ inp1
userInput <- getLine
if userInput == inp1
then do
putStrLn "correct answer"
loop inp2 inp1
else if userInput == "exit"
then do
putStrLn "exiting program"
return ()
else do
loop inp1 inp2
--filename test_program.hs
import System.IO (hSetBuffering,
hGetLine,
hPutStrLn,
BufferMode( LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
putStrLn =<< hGetLine hout
hPutStrLn hin "bla"
putStrLn =<< hGetLine hout
hPutStrLn hin "hello"
putStrLn =<< hGetLine hout
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
The problem
I have the following code
--filename main.hs
main :: IO ()
main = do
loop "hello" "bye"
loop :: String -> String -> IO ()
loop inp1 inp2 = do
putStrLn $ "Type " ++ inp1
userInput <- getLine
if userInput == inp1
then do
putStrLn "correct answer"
loop inp2 inp1
else if userInput == "exit"
then do
putStrLn "exiting program"
return ()
else do
loop inp1 inp2
After compiling it, I can run it in the terminal and type commands such as hello bye and exit. The program works.
I made a second script to interact the with the first script
--filename test_program.hs
import System.IO (hPutStrLn, hFlush, hGetLine, hClose, hSetBuffering, BufferMode( NoBuffering,LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
proc,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
hSetBuffering hout NoBuffering
putStrLn =<< hGetLine hout
hPutStrLn hin "bla"
putStrLn =<< hGetLine hout
hPutStrLn hin "hello"
putStrLn =<< hGetLine hout
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
When I run this program I expect to the following sequence of messages
- type hello
- type hello
- correct answer
- type bye
- exiting program
but nothing happens. If I rearrange the code in the following manner
--filename test_program.hs
import System.IO (hPutStrLn, hFlush, hGetLine, hClose, hSetBuffering, BufferMode( NoBuffering,LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
proc,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
hSetBuffering hout NoBuffering
hPutStrLn hin "bla"
hPutStrLn hin "hello"
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
where I place all the stdin in commands in the beginning then read out the stdout, I get to see the expected sequence of messages. My question is why does it work for one case but not the other case? In my mind, if I write all the arguments first then read the outputs, or write then read should not affect the behavior of the program.
Edit 1
Thanks to suggestion by Daniel Wagner my scripts are working as expected. I have written the scripts with the fixes below
--filename main.hs
import System.IO (hSetBuffering,
stdout,
BufferMode(LineBuffering))
main :: IO ()
main = do
hSetBuffering stdout LineBuffering
loop "hello" "bye"
loop :: String -> String -> IO ()
loop inp1 inp2 = do
putStrLn $ "Type " ++ inp1
userInput <- getLine
if userInput == inp1
then do
putStrLn "correct answer"
loop inp2 inp1
else if userInput == "exit"
then do
putStrLn "exiting program"
return ()
else do
loop inp1 inp2
--filename test_program.hs
import System.IO (hSetBuffering,
hGetLine,
hPutStrLn,
BufferMode( LineBuffering ))
import System.Process (createProcess,
waitForProcess,
shell,
StdStream(CreatePipe),
std_in,
std_out,
std_err)
import Control.Monad (when)
main :: IO ()
main = do
(Just hin, Just hout, Just herr, ph) <- createProcess (shell "./main")
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
hSetBuffering hin LineBuffering
putStrLn =<< hGetLine hout
hPutStrLn hin "bla"
putStrLn =<< hGetLine hout
hPutStrLn hin "hello"
putStrLn =<< hGetLine hout
hPutStrLn hin "exit"
putStrLn =<< hGetLine hout
putStrLn =<< hGetLine hout
_ <- waitForProcess ph
return ()
Share
Improve this question
edited Feb 17 at 17:47
greatangle
asked Feb 13 at 17:37
greatanglegreatangle
112 bronze badges
1
|
1 Answer
Reset to default 2GHC tries to help you with buffering choices when compiling a program. Roughly: when connected to a terminal, handles are line-buffered by default, to support comfy interactive use, but they are block-buffered by default for non-terminals to support efficient program-to-program transfers.
Manually choose your buffering in main.hs
to fix.
main :: IO ()
main = do
hSetBuffering stdout LineBuffering
loop "hello" "bye"
You may say: "but in test_program.hs
, I already set the buffering!". Unfortunately, each program has its own handles and does its own buffering. So you cannot set main.hs
's buffering setup from inside test_program.hs
.
本文标签: stdoutHaskell program hanging when interacting with another programStack Overflow
版权声明:本文标题:stdout - Haskell program hanging when interacting with another program - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741512599a2382692.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
hFlush System.IO.stdout
before thegetLine
there. – chi Commented Feb 13 at 17:45