Search code examples
parsinghaskellio

Input array of integers: Why is there a parse error when using the read function?


I've been trying to learn the basics of Haskell in my spare time, but I've been stuck on debugging this little program that takes integer inputs from the user, converts them into a list of Ints:

module Main where

------------------------------------------------------------

lines_to_words_impl :: [String] -> [String] -> [String]
lines_to_words_impl output_words input_lines =
    if null input_lines then output_words
    else lines_to_words_impl (output_words ++ (words (head input_lines))) (tail input_lines)

lines_to_words :: [String] -> [String]
lines_to_words input_lines =
    lines_to_words_impl [] input_lines

------------------------------------------------------------

words_to_ints_impl :: [Int] -> [String] -> [Int]
words_to_ints_impl output_ints input_words =
    if null input_words then output_ints
    else words_to_ints_impl (output_ints ++ (read (head input_words))) (tail input_words)

words_to_ints :: [String] -> [Int]
words_to_ints input_words =
    words_to_ints_impl [] input_words

------------------------------------------------------------

load_ints_from_input :: String -> [Int]
load_ints_from_input input =
    words_to_ints (lines_to_words (lines input))

------------------------------------------------------------

main :: IO ()
main = do
    -- Now, I just grab and try to print the first integer,
    -- which is enough to cause the read/parse error:
    user_input <- getContents
    putStrLn (show (head (load_ints_from_input user_input)))

But when I input some numbers it always provide the same run-time error:

Prelude.read: no parse

demo using godbolt

In my spare time over the past month I've been continually stumped. The only place I use the read function is on line 19, and its input is the head of input_words, which we know is non-empty because of where we are in the if statement - and it really should be just 7 given the example inputs in the godbolt link. From what I can tell I'm using the read function correctly (see hoogle) to read an integer string and parse it to an Int.

Could someone kindly point out why this parse error is occurring?


Solution

  • The basic reason is that Haskell thinks you are parsing a list of Ints, since you use:

    output_ints ++ (read (head input_words))
    

    so the type of read (head input_words) should be [Int], you can wrap it into a singleton list:

    output_ints ++ [read (head input_words)]
    

    to solve the problem.

    But this can be done more efficiently and readable with map:

    lines_to_words :: [String] -> [String]
    lines_to_words = concatMap words
    
    words_to_ints :: [String] -> [Int]
    words_to_ints = map read
    
    load_ints_from_input :: String -> [Int]
    load_ints_from_input = words_to_ints . lines_to_words . lines
    
    main :: IO ()
    main = do
        user_input <- getContents
        print (head (load_ints_from_input user_input))