Loop with StateT: Why this loop doesn't loop - haskell

I don't understand why this code does loop only once then exit ?
in Ghci I can answer only to the first loop then it seems the variable cont is set to false and I don't have the prompt to answer.
The result is:
*Main> testLoop1 td10
test
Do you want to continue? (y/N)
y
we continue
test
Do you want to continue? (y/N)
We stop
code:
type TDeckSTIO = StateT TableDecks IO
continue = do
putStrLn "Do you want to continue? (y/N)"
c <- getChar
return $ c == 'y'
loop1 :: TDeckSTIO ()
loop1 = do
liftIO $ putStrLn "test"
cont<- liftIO continue
if cont
then do
liftIO $ putStrLn "we continue"
liftIO $ testLoop1 td
else liftIO $ putStrLn "We stop"
testLoop1 td = runStateT (loop1 ) td >> return ()

The problem is that when you type y and hit enter, that's actually typing two characters: 'y' itself, and the newline character that gets sent by pressing the return key. The first time round, the loop sees the 'y', but the next time round, it sees the '\n', and since '\n' isn't 'y', it exits.
You can either do hSetBuffering stdin NoBuffering before you enter your loop (you'll need to import System.IO), which will let you process characters without waiting for a newline, or specifically process lines at a time:
continue = do
putStrLn "Do you want to continue? (y/N)"
s <- getLine
return $ s == "y"
By the way, instead of writing liftIO $ testLoop1 td, you can just stay in the same state monad: you can replace it with loop1 and it'll work exactly the same.
Also, testLoop1 is better written as:
testLoop1 = evalStateT loop1
evalStateT is like runStateT, but doesn't include the final state, so you don't have to explicitly discard the value with >> return ().

Related

*** Exception: user error (Prelude.readIO: no parse) in haskell

I'm trying to write a program that loops all the time waiting for an input from the user but for some reason it doesn't loop.
My program is :
charAt :: String->Char->Int
main = do
x <- readLn
if x == 1
then do
putStrLn "Word: "
word <- getLine
putStrLn "Char: "
ch <- getChar
putStrLn (show (charAt word ch))
else print "Nothing"
main
But i actually get this error:
*** Exception: user error (Prelude.readIO: no parse)
If i remove the last main calling the program will work .
does anybody knows why is that happening?
When you use getChar, it will take only a single character from your stream. However, if you've entered AEnter, the newline character '\n' is still in your stdin. A '\n' cannot get parsed into an Int, therefore you end up with that error.
You can remove that newline if you call getLine afterwards:
ch <- getChar
putStrLn (show (charAt word ch))
_ <- getLine
Or you write your own helpers:
promptInt :: IO Int
promptInt = do
putStr "Int: "
line <- getLine
case readMaybe line of
Just x -> return x
_ -> putStrLn "Try again." >> promptInt
promptChar :: IO Char
promptChar = do
putStr "Char: "
line <- getLine
case line of
[x,_] -> return x
_ -> putStrLn "Try again." >> promptChar

IO action in a when statement

Can anyone tell me why I cannot add a IO statement within a do block? This code only prints "test" and then completes execution. The last two lines do not seem to be executing.
putStrLn "Do you want to add a task. Press y to add:"
option <- getChar
when (option == 'y') $ do
print "test"
newText <- getLine
appendFile "todoList.txt" (newText ++ "\n")
getChar will peak the y char but will let the \n in the input stream.
So you need to flush the input stream before going further.
Alternatively you can use the readLn providing you define a new data type:
data Choice = Y | N
deriving (Read, Show, Eq)
putStrLn "Do you want to add a task. Press y to add:"
option <- readLn
when (option == Y) $ do
print "test"
getLine >>= appendFile "todoList.txt" . (++"\n")

Haskell - Do while loop

I'm new to Haskell and would be glad if someone would be willing to help me! I'm trying to get this program to work with a do while loop.
The result from the second getLine command gets put into the varible goGlenn and if goGlenn doesn't equal "start" then the program will return to the beginning
start = do
loop $ do lift performAction
putStrLn "Hello, what is your name?"
name <- getLine
putStrLn ("Welcome to our personality test " ++ name ++ ", inspired by the Big Five Theory.")
putStrLn "You will receive fifty questions in total to which you can reply with Yes or No."
putStrLn "Whenever you feel ready to begin please write Start"
goGlenn <- getLine
putStrLn goGlenn
while (goGlenn /= "start")
In Haskell you write "loops" recursively, most of the times.
import Control.Monad
-- ....
start = do
putStrLn "Before the loop!"
-- we define "loop" as a recursive IO action
let loop = do
putStrLn "Hello, what is your name?"
name <- getLine
putStrLn $ "Welcome to our personality test " ++ name
++ ", inspired by the Big Five Theory."
putStrLn "You will receive fifty questions in total to which you can reply with Yes or No."
putStrLn "Whenever you feel ready to begin please write Start"
goGlenn <- getLine
putStrLn goGlenn
-- if we did not finish, start another loop
when (goGlenn /= "start") loop
loop -- start the first iteration
putStrLn "After the loop!"
Not sure, maybe this version can helps you:
import Control.Monad
loop action = do
condition <- action
when condition (loop action)
while = return
start =
let action = do {
putStrLn "Hello, what is your name?";
name <- getLine;
putStrLn ("Welcome to our personality test " ++ name ++ ", inspired by the Big Five Theory.");
putStrLn "You will receive fifty questions in total to which you can reply with Yes or No.";
putStrLn "Whenever you feel ready to begin please write Start";
goGlenn <- getLine;
putStrLn goGlenn;
while (goGlenn /= "start");
}
in loop action
(Edit) or can be too:
start =
loop (do {
putStrLn "Hello, what is your name?";
name <- getLine;
putStrLn ("Welcome to our personality test " ++ name ++ ", inspired by the Big Five Theory.");
putStrLn "You will receive fifty questions in total to which you can reply with Yes or No.";
putStrLn "Whenever you feel ready to begin please write Start";
goGlenn <- getLine;
putStrLn goGlenn;
while (goGlenn /= "start");
})

Portably opening a handle to stdin many times in a single session

Code:
main = do
putStrLn "4917 Microprocessor\nEnter the Machine Code to be run: "
inp <- getContents
putStrLn "The output of the Program is:"
fState <- ((runStateT _4917) . construct . parse) inp
args <- getArgs
if elem "-v" args then putStrLn ("\nFinal state was: " ++ (show . snd) fState) else return ()
putStrLn "\n================================ RESTART ================================"
main
where parse xs = array (0,15) $
zip [0..15] $
take 16 $
map ((makeArbInt 4) . read) (words (filter ((/=)'.') xs)) ++ repeat (makeArbInt 4 0)
construct xs = Program xs z z z z 0 False
z = (makeArbInt 4 0)
There's more but this is the relevant part. Basically line 3 needs to be evaluated multiple times but getContents is closing the stdin handle:
4917: <stdin>: hGetContents: illegal operation (handle is closed)
Is there a way to reopen the handle? Or some way of preventing getContents from doing that? (Maybe I'm sending the wrong signal. I'm sending over a Ctrl-D EOF on Linux. Maybe I should use EOT or something instead?)
edit: I've managed to get the desired behaviour but it won't port to windows.
mystdin <- openFile "/dev/tty" ReadMode
inp <- hGetContents mystdin
New question: is there a generic way to portably open a handle to stdin?
You cannot prevent getContents from closing the file, and a closed file stays closed.
It seems to me that you need some other function. Usually, when you read parts of a file, you know when to stop (end of line, certain bytes on the stream). So yes: If you cannot parse the data that you are reading and detect when it is done, you should use some kind of delimiter (possibly EOT, or an empty line, or special text unlikely to occur in the data like __END__).
Have them enter a blank line to end input:
getContents2 = helper "" where
helper str = do
a <- getLine
if "" == a then return str
else helper (str++"\n"++a)
You might also haven them signal the end of the session by entering a single blank line.
To open a handle to stdin portably, use hDuplicate function on the existing stdio handle to get a new one:
mystdin <- hDuplicate stdin
inp <- hGetContents mystdin
Make sure never to close the original stdin, so that you can make more duplicates as appropriate. (I'm not sure if this is good Haskell style)

haskell -skipping getLine

hey - great coders and haskellers,
i'm a haskell freshman and have a problem with a program
it boils down to the following situaition
main :: IO ()
main = do
putStrLn "\nplease give me some input"
input1 <- getLine
putStrLn "\nplease give me another input"
input2 <-getLine
putStrLn ("\nyour inputs were "++show(input1)++" and "++ show(input2)")
putStrLn "restart ?? yY or nN"
c <- getChar
restart c
where
restart c
|elem c "yY" = do
main
|elem c "nN" = putStrLn "\nExample Over"
|otherwise = do
putStrLn "\nyou must type one of Yy to confirm or nN to abort"
c'<- getChar
restart c'
on any but the first execution of main
input1 <- getLine
is skipped and i can find no reason for it, as the following
input2 <- getLine
is executed as expected, i'm open for any suggestions and help
thanks in advance ε/2
The fix: set NoBuffering at the start of your program:
hSetBuffering stdin NoBuffering
Why does this fix the issue? Look at what you're typing when you don't using NoBuffering! You type, and getLine consumes:
first input[enter]
Then you type, and getLine #2 consumes:
second input[enter]
Then you type:
y[enter]
But getChar only consumed the y and leaves the [enter] buffered, which your first getLine call reads! Why did you type [enter]? Because you had to, just hitting 'y' didn't cause main to loop because the terminal was line buffered.

Resources