-4

My search function works properly when the input is a number, but crashes when it is not. What can I add to my code to prevent that from happening?

searchAge = do
  putStrLn "\n Please type the age of the person you're looking for: \n"
  age <- getLine
  input <- readFile $ "databas.txt"
  putStrLn "\n Here are all the people that matches your search: \n"
  let people = parse input
      output = map personToString (filter (\p -> personAge p == read age) people)
    in putStrLn (unlines output)

putStrLn "Let's get back to the search menu again!"     
searchDatabase
dfeuer
  • 48,079
  • 5
  • 63
  • 167
prawniie
  • 9
  • 1
  • 3
    It would kinda help if you showed us how you approached the problem so far, i.e., code please... – leftaroundabout Feb 23 '16 at 15:11
  • 4
    Blind guessing: replace `read` with `readMaybe`, and handle the error case (`import Text.Read` first). You should really provide more information in your question. – chi Feb 23 '16 at 15:20
  • 10
    Listen up, oh younger one, / as this is a maddening song. / Take a look and you will see / `read :: String -> Int` is wrong for ye. / It's type is wrong, it's result is unknown / if you use it on a random string like `"frown"`. / But here is for a final hint / you're looking for `readMaybe :: String -> Maybe Int`. – Zeta Feb 23 '16 at 18:10
  • 2
    lol ... there should be a platinum badge for answers/comments like this ... **yo** – Random Dev Feb 23 '16 at 21:14
  • 1
    @Zeta, why isn't that an answer? – dfeuer Feb 24 '16 at 00:47
  • @dfeuer: Because I also wanted to improve the question's quality, but didn't have the time yesterday. – Zeta Feb 24 '16 at 06:43
  • 2
    @Carsten Time for a new challenge: Write at least one answer per week as a poem. – Zeta Feb 24 '16 at 08:56
  • The indentation doesn't really make sense. Please *add* any that you need. – dfeuer Feb 25 '16 at 16:16
  • 2
    @Carsten: Second week, [second poem](http://stackoverflow.com/a/35733380/1139697) :D. – Zeta Mar 02 '16 at 07:58

1 Answers1

15

Listen up, oh younger one,
as this is a maddening song.
Take a look and you will see
read :: String -> Int is wrong for ye.

Its type is wrong, its result is unknown;
if been used on a string like "frown".
But here is for the final hint
you're looking for readMaybe :: String -> Maybe Int.


The problem with read is that there is no notion of failure in its type signature:

read :: Read a => String -> a

What happens if we set a to Int and try to use it on the string "frown"? It will result in an exception:

ghci> read "frown" :: Int
*** Exception: Prelude.read: no parse

After all, what should it return? Anything from the domain of Int is a valid value.

Enter readMaybe :: Read a => String -> Maybe a. Now the potential error is covered in the type signature, and it does not result in an exception anymore:

ghci> import Text.Read
ghci> readMaybe "frown" :: Maybe Int
Nothing
ghci> readMaybe "15" :: Maybe Int
Just 15

We can wrap this in a new function called getNumber that repeatedly asks for an Int until the user actually provides one:

getNumber :: IO Int
getNumber = do
  line <- getLine
  case (readMaybe line :: Maybe Int) of
     Just x  -> return x
     Nothing -> putStrLn "Please enter a number!" >> getNumber

You can then use it to get an Int instead of a String for your age:

searchAge = do
    ...
    age   <- getNumber
    input <- readFile "databas.txt"
    putStrLn "\n Here are all the people that matches your search: \n"
    let people = parse input
        output = map personToString (filter (\p -> personAge p == age) people)
    in putStrLn (unlines output)
Zeta
  • 103,620
  • 13
  • 194
  • 236
  • 1
    @dfeuer OK, OK, I will rollback / but please just keep your rep in check / while a bounty would be fine / this reputation should not be mine. / A new who knows more than me / would lighten up with joy and glee. / But if you think it's __that__ well done / your gift is certainly welcome. – Zeta Feb 25 '16 at 17:25