I am using interact
to process some user inputs step-by-step (specifically, it's a chess program). However, I haven't found a way to deal with the situation where the user might want to just break out of the loop and start this match of chess from the beginning.
When I'm executing a normal procedure in ghci, pressing Ctrl-C
will not exit the whole ghci, but will just stop the procedure itself and allow me to go on with some other procedures. However, if I press Ctrl-C
in the console with the interact
function in effect, the following message is shown:
^CInterrupted.
*Main>
<stdin>: hGetChar: illegal operation (handle is closed)
And then I have to launch ghci all over again.
I also thought of catching special user inputs such as "exit", however, since the type of interact
is interact :: (String -> String) -> IO ()
, the input will have to go through the function typed (String -> String)
first, and I haven't found a way for that function to notify the main IO that it should quit.
How should I break out of interact
? Or is interact
not intended to be used this way and I should compose custom IO functions?
How should I break out of
interact
?
You can't. You can think of interact f
as getContents >>= putStrLn . f
. And getContents
will close the handle on stdin
. Any further operation concerning reading will fail.
The literal character ^D gets shown in the terminal
That's a problem with readline. GHCi changes the buffering method of stdin
from LineBuffer
to NoBuffering
to use readline optimally. If you want to exit interact
with ^D
, you need to change the buffering method:
ghci> import System.IO
ghci> hGetBuffering stdin
NoBuffering
ghci> hSetBuffering stdin LineBuffering
ghci> interact id
hello world
hello world
pressing control-D after the next RETURN
pressing control-D after the next RETURN
<stdin>: hGetBuffering: illegal operation (handle is closed)
Or is
interact
not intended to be used this way and I should compose customIO
functions?
Yes, it's not intended to be used this way. interact
is meant to use all input and dictate all output. If you want to use use line-wise input, you can write your own line-wise interact method (or use an external library):
import Control.Monad (when)
interactLine :: (String -> String) -> IO ()
interactLine f = loop
where
loop = do
l <- getLine
when (l /= "quit") $ putStrLn (f l) >> loop
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With