Why isEOF doesn't work? - haskell

Here is minimal complete example:
import Control.Monad
import System.IO
loop :: IO ()
loop =
do line <- getLine
putStrLn line
eof <- isEOF
unless eof loop
main = loop
This program is supposed to read a line, print it out, stop if there is 'end of file' character in stdin. It doesn't leave the loop at all.
If I put eof <- isEOF before putStrLn line the program behaves very strange (try it!). I cannot get it at all: how putStrLn can possibly affect input stream and why doesn't the program terminate when I put 'end of file' character into stream (with Ctrl+D)?
Description of program's behavior when eof <- isEOF goes before putStrLn line:
After entering of a line, program does not print the entered line, but expects more input. As it gets more input, it starts to print previously entered lines. This is log of a test:
foo
boo
output: foo
bar
output: boo
baz
output: bar
< here I press Ctrl-D >
output: baz
Source:
import Control.Monad
import System.IO
loop :: IO ()
loop =
do line <- getLine
eof <- isEOF
putStrLn $ "output: " ++ line
unless eof loop
main =
do hSetBuffering stdin LineBuffering
loop

From http://lambda.haskell.org/platform/doc/current/ghc-doc/libraries/haskell2010-1.1.1.0/System-IO.html#g:11:
NOTE: hIsEOF may block, because it has to attempt to read from the stream to determine whether there is any more data to be read.
The putStrLn doesn't affect the isEOF, but the isEOF prevents the program from getting to the putStrLn before more characters are available, or you have actually pressed ^D.
So you should never use hIsEOF/isEOF until the point in the program where you are ready to read more characters if there are any.

Related

putStr and putStrLn messing with the output [duplicate]

This question already has an answer here:
Wrong IO actions order using putStr and getLine
(1 answer)
Closed 1 year ago.
I was learning haskell from Learn You a Haskell For Great Good book. There was this code
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
when i am running this code in gitbash at first it is just asking for any input after giving the input text and hitting enter( say the input text was soham) it is showing Give me some input: SOHAM.
Then i changed the code to
import Control.Monad
import Data.Char
main = forever $ do
putStrLn "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
and after running it is showing me Give me some input: and asking for an input. after giving the same input soham it is showing SOHAM
Again changing the code to
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStr $ map toUpper l
It is just taking input again and again and when i am pressing the end of file key(ctrl+C) it is showing all the output one after another side by side but the out puts are like the original code.
Why such variations are happening ?
This is likely due to buffering: with LineBuffering it will flush in whenever a new line is output. This thus means that if you use putStr, and the string itself does not contain a new line '\n' character, it will buffer the ouput and wait until a new line is written to actually write the output to the console
You can set it to NoBuffering to write the content immediately to the console. You can change the buffering settings for the stdout with hSetBuffering :: Handle -> BufferMode -> IO ():
import Control.Monad
import Data.Char
import System.IO
main = do
hSetBuffering stdout NoBuffering
forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
another option is to flush the buffer only for putStr with hFlush :: Handle -> IO (), and thus not change the buffering policy itself:
import Control.Monad
import Data.Char
import System.IO
main = do $ forever
putStr "Give me some input: "
hFlush stdout
l <- getLine
putStrLn $ map toUpper l

Not all code in do block seems to be executed [duplicate]

I'm a Haskell beginner, I'm just beginning to wrap my head around Monads, but I don't really get it yet. I'm writing a game that consists of asking the user for input, and responding. Here is a simplified version of my function:
getPoint :: IO Point
getPoint = do
putStr "Enter x: "
xStr <- getLine
putStr "Enter y: "
yStr <- getLine
return $ Point (read xStr) (read yStr)
completeUserTurn :: (Board, Player) -> IO (Board, Player)
completeUserTurn (board, player) = do
putStr $ "Enter some value: "
var1 <- getLine
putStr $ "Enter another value: "
var2 <- getLine
putStr $ "Enter a point this time: "
point <- getPoint
if (... the player entered legal values ...) then do
putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) "
continue <- getLine
if continue == "y" then
return (...updated board..., ...updated player...)
else
completeUserTurn (board, player)
else do
putStr "Invalid Move!\n"
completeUserTurn (board, player)
What's happening is that the prompts will appear out of order with the text that is supposed to appear before the prompt.
Here's an example of what's happening after I compiled the code above:
1
Enter some value: Enter another value:2
3
4
Enter a point this time: Enter x: Enter y: y
Is this correct? (y/n):
The bold are the things I typed in.
Obviously, I have some major conceptual error, but I don't know what. Note that it works correctly in the interpreter and fails when compiled.
As Michael said, the issue is buffering. By default, output is buffered until you print a newline (or until the buffer is full if you have really long lines), so you'll most often see this issue when trying to do same-line prompts using putStr like you're doing.
I suggest defining a small helper function like this to take care of doing the flushing for you:
import System.IO
prompt :: String -> IO String
prompt text = do
putStr text
hFlush stdout
getLine
Now you can simply do
getPoint = do
xStr <- prompt "Enter x: "
yStr <- prompt "Enter y: "
return $ Point (read xStr) (read yStr)
The IO is happening in the correct order. The issue is buffering. If you flush stdout after each putStr, it should work as expecting. You'll need to import hFlush and stdout from System.IO.
The problem wasn't with the order of operations in the IO code. The issue was input and output is by default buffered when using stdin and stdout. This increases the performance of IO in an app, but can cause operations to appear to occur out of order when both stdin and stdout are used.
There is two solutions to this. You can use the hFlush method to force a handle (either stdin or stdout) to be flushed. Eg hFlush stdout, hFlush stdin. A simpler solution (which works fine for interactive apps) is to disable buffering altogether. You can do this by calling the methods hSetBuffering stdout NoBuffering and hSetBuffering stdin NoBuffering before you start your program (ie put those lines in your main method.

IO happens out of order when using getLine and putStr

I'm a Haskell beginner, I'm just beginning to wrap my head around Monads, but I don't really get it yet. I'm writing a game that consists of asking the user for input, and responding. Here is a simplified version of my function:
getPoint :: IO Point
getPoint = do
putStr "Enter x: "
xStr <- getLine
putStr "Enter y: "
yStr <- getLine
return $ Point (read xStr) (read yStr)
completeUserTurn :: (Board, Player) -> IO (Board, Player)
completeUserTurn (board, player) = do
putStr $ "Enter some value: "
var1 <- getLine
putStr $ "Enter another value: "
var2 <- getLine
putStr $ "Enter a point this time: "
point <- getPoint
if (... the player entered legal values ...) then do
putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) "
continue <- getLine
if continue == "y" then
return (...updated board..., ...updated player...)
else
completeUserTurn (board, player)
else do
putStr "Invalid Move!\n"
completeUserTurn (board, player)
What's happening is that the prompts will appear out of order with the text that is supposed to appear before the prompt.
Here's an example of what's happening after I compiled the code above:
1
Enter some value: Enter another value:2
3
4
Enter a point this time: Enter x: Enter y: y
Is this correct? (y/n):
The bold are the things I typed in.
Obviously, I have some major conceptual error, but I don't know what. Note that it works correctly in the interpreter and fails when compiled.
As Michael said, the issue is buffering. By default, output is buffered until you print a newline (or until the buffer is full if you have really long lines), so you'll most often see this issue when trying to do same-line prompts using putStr like you're doing.
I suggest defining a small helper function like this to take care of doing the flushing for you:
import System.IO
prompt :: String -> IO String
prompt text = do
putStr text
hFlush stdout
getLine
Now you can simply do
getPoint = do
xStr <- prompt "Enter x: "
yStr <- prompt "Enter y: "
return $ Point (read xStr) (read yStr)
The IO is happening in the correct order. The issue is buffering. If you flush stdout after each putStr, it should work as expecting. You'll need to import hFlush and stdout from System.IO.
The problem wasn't with the order of operations in the IO code. The issue was input and output is by default buffered when using stdin and stdout. This increases the performance of IO in an app, but can cause operations to appear to occur out of order when both stdin and stdout are used.
There is two solutions to this. You can use the hFlush method to force a handle (either stdin or stdout) to be flushed. Eg hFlush stdout, hFlush stdin. A simpler solution (which works fine for interactive apps) is to disable buffering altogether. You can do this by calling the methods hSetBuffering stdout NoBuffering and hSetBuffering stdin NoBuffering before you start your program (ie put those lines in your main method.

read line until ESC button in pressed in haskell

I need to real lines until ESC button is pressed. How can I check it?
lines
= do
line <- getLine
if (== "/ESC") --this condition is wrong
then ...
else do
ln <- lines
return ...
Could anybody fix my problem?
The correct way to escape is with a backslash, the character is '\ESC', so the condition would be
if line == "\ESC"
But I'm not sure every terminal passes an '\ESC' through to the application.
If you want to stop immediately when the ESC key is pressed, something along the lines of
module Main (main) where
import System.IO
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
getUntilEsc ""
getUntilEsc :: String -> IO ()
getUntilEsc acc = do
c <- getChar
case c of
'\ESC' -> return ()
'\n' -> do putStrLn $ "You entered " ++ reverse acc
getUntilEsc ""
_ -> getUntilEsc (c:acc)
is what you need. You have to read character-wise, and you need to turn off the buffering of stdin, so the characters are immediately available for reading, and not only after a newline has been entered.
Note that on Windows turning off buffering didn't work. I don't know if this has been fixed recently.
Also, as #Daniel Wagner reported, it may well be that the Windows command prompt doesn't pass the ESC to the application.

Handling UserInterrupt exception in Haskell

I'm implementing a REPL for a Scheme interpreter in Haskell and I'd like to handle some async events like UserInterrupt, StackOverflow, HeapOverflow, etc... Basically, I'd like to stop the current computation when UserInterrupt occurs and print a suitable message when StackOverflow and HeapOverflow occur, etc. I implemented this as follows:
repl evaluator = forever $ (do
putStr ">>> " >> hFlush stdout
out <- getLine >>= evaluator
if null out
then return ()
else putStrLn out)
`catch`
onUserInterrupt
onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption"
onUserInterrupt e = throw e
main = do
interpreter <- getMyLispInterpreter
handle onAbort (repl $ interpreter "stdin")
putStrLn "Exiting..."
onAbort e = do
let x = show (e :: SomeException)
putStrLn $ "\nAborted: " ++ x
It works as expected with one exception. If I start the interpreter and press Ctrl-Z + Enter, I get:
>>> ^Z
Aborted: <stdin>: hGetLine: end of file
Exiting...
That's correct. But if I start the interpreter and press Ctrl-C followed by Ctrl-Z + Enter I get:
>>>
UserInterruption
>>> ^Z
And it hangs and I can't use the interpreter anymore. However, if I press Ctrl-C again, the REPL unblocks. I searched a lot and I can't figure out the reason of it. Can anyone explain me?
Many thanks!
Control-C handling does not work with catch: may be related to GHC #2301: Proper handling of SIGINT/SIGQUIT
Here is a working testcase, with the evaluator removed:
module Main where
import Prelude hiding (catch)
import Control.Exception ( SomeException(..),
AsyncException(..)
, catch, handle, throw)
import Control.Monad (forever)
import System.IO
repl :: IO ()
repl = forever $ (do
putStr ">>> " >> hFlush stdout
out <- getLine
if null out
then return ()
else putStrLn out)
`catch`
onUserInterrupt
onUserInterrupt UserInterrupt = putStrLn "\nUserInterruption"
onUserInterrupt e = throw e
main = do
handle onAbort repl
putStrLn "Exiting..."
onAbort e = do
let x = show (e :: SomeException)
putStrLn $ "\nAborted: " ++ x
On Linux, Control-Z is not caught as Sjoerd mentioned. Perhaps you are on Windows, where Control-Z is used for EOF. We can signal EOF on Linux with Control-D, which replicates the behavior you saw:
>>> ^D
Aborted: <stdin>: hGetLine: end of file
Exiting...
EOF is handled by your handle/onAbort function, and Control-C is handled by catch/onUserInterrupt. The issue here is that your repl function will only catch the first Control-C -- the testcase can be simplified by removing the handle/onAbort function. As noted above, that Control-C handling does not work with catch may be related to GHC #2301: Proper handling of SIGINT/SIGQUIT.
The following version instead uses the Posix API to install a persistent signal handler for Control-C:
module Main where
import Prelude hiding (catch)
import Control.Exception ( SomeException(..),
AsyncException(..)
, catch, handle, throw)
import Control.Monad (forever)
import System.IO
import System.Posix.Signals
repl :: IO ()
repl = forever $ do
putStr ">>> " >> hFlush stdout
out <- getLine
if null out
then return ()
else putStrLn out
reportSignal :: IO ()
reportSignal = putStrLn "\nkeyboardSignal"
main = do
_ <- installHandler keyboardSignal (Catch reportSignal) Nothing
handle onAbort repl
putStrLn "Exiting..."
onAbort e = do
let x = show (e :: SomeException)
putStrLn $ "\nAborted: " ++ x
which can handle Control-Cs being pressed multiple times:
>>> ^C
keyboardSignal
>>> ^C
keyboardSignal
>>> ^C
keyboardSignal
If not using the Posix API, installing a persistent signal handler on Windows requires re-raising the exception each time it is caught, as described in http://suacommunity.com/dictionary/signals.php

Resources