I had written the simple program below, convinced it would absorb all keystrokes I hit and, when I hit q, it would print the previous keys as a single string.
import System.IO
main = do
hSetBuffering stdin NoBuffering
hSetEcho stdin False
x <- takeWhile (/= 'q') <$> sequence (repeat getChar)
print x
But that's not the case.
It's not that keys are not read, as it's obvious when I change the longest line to
x <- takeWhile (/= 'q') <$> sequence (repeat (getChar >>= \c -> print c >> return c))
which does print the characters one by one as I type them; but the q doesn't cause the loop to exit.
My understanding is that sequence
is not lazy, in the sense that it will try to run the infinite list of getChar
actions before takeWhile
has a chance to start its job.
If that's the case, then what other options do I have?
If the intent is clear form the snippet above, I can add that in the real application getChar
would be swapped with a function that do
es c <- getChar
and then makes a processing on it.
You may consider a little helper action for that purpose:
getInput :: Char -> IO Char
getInput previous = do
c <- getChar
if c == 'q' then return previous else getInput c
This would enable you to get x
like
x <- getInput ' '
or with some other appropriate default value instead of ' '
.
If you truly want a one-liner, you can always use fix
to convert any recursive action into a lambda expression.
First, then,
import Data.Function
and then
x <- (fix $ \rec previous -> getChar >>= \c -> if c == 'q' then return previous else rec c) ' '