First of all, you should be opening the file with openFile, then reading the file with readFile. You only need the latter, it is a handy function that opens the file, reads it, returns the contents, then closes the file safely for you.
What you should be aiming to write is a pure function
processContents :: String -> Maybe (Float, [(String, Float)])
that can get the first number from the file and then each successive line. Since we want to ensure our code is robust, we should also handle failures in some way. For this problem, I think using Maybe is sufficient to handle errors in parsing. For this, we'll add a couple imports:
import Text.Read (readMaybe)
import Data.Maybe
processContents :: String -> Maybe (Float, [(String, Float)])
processContents c = do
let ls = lines c
-- If we can't get a single line, the whole operation fails
firstLine <- listToMaybe ls
-- If we can't parse the total, the whole operation fails
total <- readMaybe firstLine
let rest = drop 1 ls
return (total, catMaybes $ map parseLine $ rest)
The listToMaybe function acts as a "safe head", it is defined as
listToMaybe (x:xs) = Just x
listToMaybe [] = Nothing
The readMaybe function has the type Read a => String -> Maybe a, and returns Nothing if it couldn't parse the value, making it easy to use in this case. The catMaybes function takes a list of Maybe as, extracts all the ones that aren't Nothing, and returns the rest as a list with the type catMaybes :: [Maybe a] -> [a].
Now we just need to implement parseLine:
parseLine :: String -> Maybe (String, Float)
parseLine line = case words line of
(book:costStr:_) -> do
cost <- readMaybe costStr
return (book, cost)
_ -> Nothing
This breaks the line up by spaces, grabs the first two elements, and parses them into a name and a number, if possible.
Now you have a function that could parse your example file into the value
Just (2000.0, [("booka", 500.0), ("bookb", 1000.0), ("bookc", 250.0), ("bookd", 250.0)])
Now it's just up to you to figure out how calculate the percentages and print it to the screen in your desired format.