IO happens out of order when using getLine and putStr - haskell

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.

Related

Getting putStrLn to work inside an if-else block

I am writing a palindrome solution in Haskell, and I want the function to show an error if a null is entered. I do not want to use the error function as that halts the program. Hence, I want to show an error message using putStrLn and continue the loop.
I have tried using show to change the input given to the putStrLn but it doesn't work and throws compile time type-error.
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then
-- putStrLn "This is not a word!"
main
else do
putStrLn $ show $ checkPalindrome word
main
checkPalindrome w = if reverse w == w then True else False
I expect it to show an error, but it only gives an error. What are possible solutions to show a halt-safe error?
If you write both a putStrLn "this is not a word!" and a main, you should use a do block here:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then do
putStrLn "This is not a word!"
main
else do
putStrLn $ show $ checkPalindrome word
main
That being said, you can simplify the above by making a call at the bottom of the do block of the main:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
if null word
then putStrLn "This is not a word!"
else putStrLn $ show $ checkPalindrome word
main
or we can, like #Bergi says, even put more in the main block, like:
main = do
putStrLn "Hey there, What's up! ENTER WORD TO CHECK PALINDROME!"
word <- getLine
putStrLn $ if null word
then "This is not a word!"
else show $ checkPalindrome word
main
If you write this without do block, Haskell aims to parse putStrLn "This is not a word!" main. This thus means that putStrLn is supposed to have type String -> IO a -> IO a, but that is not the case.
By using a do block, Haskell will desugar the do block [Haskell'10 report] into putStrLn "This is not a word!" >> main, and this is sound (at least for the type system). Since the bind operator has type (>>) :: Monad m => m a -> m b -> m b.

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.

How can I bind two IO () monads without executing them?

In the below code, I am using >> to concatenate IO actions together. But AFAIU, m1>>m2 gets de-sugared to m1>>=(\_.m2) and thus it is executing the first IO action right when it is binding. I want all printing to happen in the main, i.e. print statements should not get interleaved with the input statements ("Enter Code"). Since do doesn't allow me to return any other monad than IO like [IO ()]. How can I have the desired printing effect?
f :: [Int] -> IO ()
f inventory = do
putStrLn "Enter Code\n"
x <- getLine
let idx = nameToIndex x
putStrLn "Quantity\n"
y <- getLine
putStrLn "More?\n"
c <- getChar
let q = (read y :: Int)
let curM = if inventory !! idx >= q then (putStrLn "sdaf\n") else (putStrLn "Overflow!\n")
if c == 'Y' then curM>>(f (update inventory idx)) else curM
main = f [1, 2]
I'm not 100% sure I understand the problem, but I think it goes like this: you'd like to do some interactions with the user, storing up information about the interaction, then display all the information at once at the end of the entire interaction.
Here's a very simplified version of your code, that skips all the business logic and just keeps asking the user if they want to continue.
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> putStrLn "Okay, let's continue." >> prompt
_ -> return ()
main = prompt
I think the effect you're asking for is to delay the display of "Okay, let's continue." until the user has stopped hitting "y". That's no problem. There's lots of ways you can do this. The most flexible is to have prompt return the action it wants to be executed after it completes:
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> do
act <- prompt
return (putStrLn "Okay, let's continue." >> act)
_ -> return (return ())
main = do
act <- prompt
act
(There are combinators that can make this code more compact, as well.) But I don't like this design; it makes it difficult to introspect on the result of prompt. A more specialized but also more maintainable approach is to return some data describing the interaction, which the caller can then turn into an IO action summarizing things. In this case, a list of strings seems like a suitable description.
prompt = do
putStrLn "Continue?"
s <- getLine
case s of
"y" -> do
results <- prompt
return ("Okay, let's continue." : results)
_ -> return []
main = do
results <- prompt
mapM_ putStrLn results
Hopefully this explanation is clear enough that you can combine this idea with your more complicated business logic.

Why isEOF doesn't work?

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.

Haskell: Basic Reading Int

The objective is to code the game of Nim in Haskell as a school assignment. I'm new to Haskell and get weird behavior when I try to read input.
The goal is to read two integers. Instead of printing the first message, then prompting and then going on to the second one, it just prints the two messages and I'm unable to give proper input. What is wrong here?
type Board = [Int] -- The board
type Heap = Int -- id of heap
type Turn = (Int, Int) -- heap and number of stars to remove
readInt :: String -> IO Int
readInt msg = do putStr (msg ++ "> ")
inp <- getChar
let x = ord inp
return x
readTurn :: Board -> IO(Turn)
readTurn b = do heap <- readInt "Select heap:"
amt <- readInt "Select stars:"
print heap
print amt
return(heap, amt)
The problem is that stdout is line-buffered by default, which means that nothing gets output until you print a newline. There are two ways to solve this:
Use hFlush stdout after printing the prompt to flush the buffer.
Use hSetBuffering stdout NoBuffering at the start of your program to disable output buffering.
Also, using getChar and ord will read a single character and give you its ASCII value, which is probably not what you wanted. To read and parse a number, use readLn:
import System.IO (hFlush, stdout)
readInt :: String -> IO Int
readInt msg = do
putStr (msg ++ "> ")
hFlush stdout
readLn
readChar reads only one character at a time. I assume you want instead to read a whole line, convert it to a number (possibly with more than one digit), and continue. You need to use getLine :: IO String and read :: Read a => String -> a:
readInt :: String -> IO Int
readInt msg = do
putStr (msg ++ "> ")
hFlush stdout
inp <- getLine
return (read inp)

Resources