Shawn
Shawn

Reputation: 35

readIO parse error haskell, this close

So I've been enjoying this challenging language, I am currently working on an assignment for school.

This is what it says: I need to prompt the user for a list of numbers, then display the average of the list , I am so close to figuring it out. However I get this weird parse error:

"Exception: user error (Prelude.readIO: no parse)" 

Here is my code:

module Main (listM', diginums', getList, main) where 

import System.IO     
import Data.List

diginums' = []
listM' = [1, 2, 3]
average' = (sum diginums') / (fromIntegral (length diginums'))
getList :: IO [Double]
getList = readLn

main = do
      putStrLn "Please enter a few numbers" 
      diginums' <- getList
      putStrLn $ show average' 

Terminal Prompts : Enter a few #'s

I Enter : 123

ERROR : Exception: user error (Prelude.readIO: no parse)

I know my functions are working correctly to calculate the average. Now I think my problem is that when I take in the list of numbers from the user, I don't correctly parse them to type Double for my average function.

Upvotes: 1

Views: 1175

Answers (2)

Peter Hall
Peter Hall

Reputation: 58735

There are no mutable variables in Haskell, but it looks like you are trying to initialise diginums' as an empty list and then populate it with getList.

Instead, maybe you want to pass the list of numbers to average' as an argument, something like:

module Main (getList, main) where

import System.IO
import Data.List


average' ds = (sum ds) / (fromIntegral (length ds))
getList :: IO [Double]
getList = readLn

main = do
      putStrLn "Please enter a few numbers"
      diginums' <- getList
      putStrLn $ show $ average' diginums'

Also, as Daniel said, you need to input using Haskell literal List syntax, given the way you've coded it.

Upvotes: 3

Daniel Fischer
Daniel Fischer

Reputation: 183888

Your type signature says that

getList :: IO [Double]
getList = readLn

reads a list of Doubles, that means it expects input of the form

[123, 456.789, 1011.12e13]

but you gave it what could be read as a single number, "123". Thus the read fails, the input couldn't be parsed as a [Double].

If you want to parse input in a different form, not as syntactically correct Haskell values, for example as a space-separated list of numbers, you can't use readLn, but have to write a parser for the desired format yourself. For the mentioned space-separated list of numbers, that is very easy, e.g

getList :: IO [Double]
getList = do
    input <- getLine
    let nums = words input
    return $ map read nums

If you want to get the list in the form of numbers each on its own line, ended by an empty line, you'd use a recursion with an accumulator,

getList :: IO [Double]
getList = completeList []

completeList :: [Double] -> IO [Double]
completeList acc = do
    line <- getLine
    if null line
        then return (reverse acc)
        else completeList (read line : acc)

Parsers for more complicated or less rigid formats would be harder to write.

When the parsing is fixed, you run into the problem that you haven't yet got used to the fact that values are immutable.

diginums' = []
listM' = [1, 2, 3]
average' = (sum diginums') / (fromIntegral (length diginums'))

defines three values, the Double value average' is defined in terms of the empty list diginums', hence its value is

sum diginums' / fromIntegral (length diginums') = 0 / 0

which is a NaN.

What you need is a function that computes the average of a list of Doubles, and apply that to the entered list in main.

average :: [Double] -> Double
average xs = sum xs / fromIntegral (length xs)

main = do
    putStrLn "Please enter a few numbers" 
    numbers <- getList
    print $ average numbers

Upvotes: 3

Related Questions