Haskell ReplicateM IO
-
12-12-2019 - |
Question
I'm trying to create a function which allows the user to input a list of strings. The function takes the length and allows the user to input length-1 more lines. Then each line is checked to ensure it is the same length as the original line. However, I'm having a few problems and that I can't find a solution to.
The problems are that I can input more than count-1 lines and the length isn't being calculated as I expected.. for example if I input ["12","13"] and then ["121","13"] the error is given, although they are the same length!
read :: IO [Line]
read = do
line <- getLine
let count = length line
lines <- replicateM (count-1) $ do
line <- getLine
if length line /= count
then fail "too long or too short"
else return line
return $ line : lines
Line is of type String.
readLn gives a parse error.
Solution
It sounds to me like you're getting confused about the difference between getting a line as a String
and reading/parsing a line of input as a custom type. You are using getLine
, which always returns exactly the String
that the user types. Compare:
Prelude> fmap length getLine
["12","13"]
11
Prelude> length "[\"12\",\"13\"]" -- explanation of the 11
11
Prelude> fmap length (readLn :: IO [String])
["12","13"]
2
Prelude> length ["12", "13"] -- explanation of the 2
2
As demonstrated here, you probably want to use readLn
, which first gets a line of input and then parses it with read
.
-- defined in the Prelude
readLn = do
s <- getLine
return (read s)
If I modify your code to include the imports and definitions below:
import Control.Monad
type Line = [String]
...and to call readLn
instead of getLine
, then I can type the literal lines ["12","13"]
and ["121","13"]
without errors.