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
interactnot intended to be used this way and I should compose customIOfunctions?
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