Reputation: 39896
I'm playing with the interact
function from Prelude, wanting to do a simple REPL evaluating my inputs line by line, and I cannot understand what's going on.
If I make it simple like this:
main :: IO ()
main = interact interaction
interaction :: String -> String
interaction (x:xs) = xs
interaction x = x
Then it behaves ok, and removes the first character from my input, or returns the input if it is only one character long.
What puzzles me is if add this line:
interaction :: String -> String
interaction x | length x > 10 = "long word" -- this line causes problem
interaction (x:xs) = xs
interaction x = x
Then, interact seems not to work correctly any longer. It just awaits for my input, swallows it awaiting another input and so on, but never outputs anything.
It seems so simple however, but I cannot see what is going wrong. Any idea ?
(On my path I have GHC 7.6.3, I don't know if it has some importance.)
Upvotes: 1
Views: 107
Reputation: 29100
With what you've written, you're trying to calculate the length of the whole input sequence, so your program has to wait for the entire sequence to be available.
You could try a lazy pattern-match like this:
interaction (x1:x2:x3:x4:x5:x6:x7:x8:x9:x10:x11:_) = "long word"
This allows you to ignore the rest of the input once you know you've reached 10 characters.
A cleaner/more general alternative (suggested by @amalloy) that scales for bigger lengths and allows a variable length guard would be something like:
interaction xs | not . null . drop 10 $ xs = "long word"
If what you really want to do is process your input a line at a time, and produce this message for an individual line longer than 10 characters, you can use lines
and unlines
to make your interaction function line-oriented rather than character-oriented, e.g.:
main :: IO ()
main = interact (unlines . interaction . lines)
interaction :: [String] -> [String]
interaction (x:_) | length x > 10 = "long word" -- this is just looking at the first line
...
or maybe if you want to do that for every line, not just the first:
main :: IO ()
main = interact (unlines . map interaction . lines)
interaction :: String -> String
interaction x | length x > 10 = "long word"
...
Upvotes: 3
Reputation: 91837
interact
takes the entirety of standard input at once, as one big string. You call length
on all of stdin, and so your function cannot return until stdin is exhausted entirely. You could, for example, hit ctrl-D (assuming Unix) to send EOF, and then your function will finally find out what stdin's length is.
Upvotes: 3