How to Use reads

Albert Y. C. Lai, trebla [at] vex [dot] net

You have a String, it stands for a number or it is garbage, you want to parse out that number or call it a parse error. How to do it with read? Answer: use reads instead.

Principle

The principle of using reads is exemplified by the following cases:

Examples

The following allows trailing garbage, e.g., 34abc is accepted:

main = do
  cs <- getLine
  case reads cs :: [(Integer,String)] of
    [(n, _)] -> print n
    _ -> putStrLn "invalid input"

The following bans trailing data, e.g., 34abc is rejected, since the pattern insists on empty trailing data; the user must enter 34:

main = do
  cs <- getLine
  case reads cs :: [(Integer,String)] of
    [(n, "")] -> print n
    _ -> putStrLn "invalid input"

Or perhaps you expect a number followed by a boolean, and that should be the end:

main = do
  cs <- getLine
  case reads cs :: [(Integer,String)] of
    [(n, s1)] -> case reads s1 of
      -- type anontation unneeded because "not b" helps infer Bool
      [(b, "")] -> putStrLn (show n ++ " " ++ show (not b))
      [(_, _)] -> putStrLn "invalid, trailing data"
      _ -> putStrLn "invalid second item"
    _ -> putStrLn "invalid first item"

Another way, but this time you don't care where the parse error is:

main = do
  cs <- getLine
  case myreads cs :: [(Integer,Bool,String)] of
    [(n, b, "")] -> putStrLn (show n ++ " " ++ show (not b))
    _ -> putStrLn "invalid input"

myreads cs0 = [ (x, y, cs2) | (x, cs1) <- reads cs0, (y, cs2) <- reads cs1 ]

Lastly, if you expect unique parses, you may as well forget lists and turn everything into Maybe:

main = do
  cs <- getLine
  case myparse cs :: Maybe (Integer,Bool) of
    Just (n, b) -> putStrLn (show n ++ " " ++ show (not b))
    Nothing -> putStrLn "invalid input"

myparse cs0 = case [ (x, y, cs2) | (x, cs1) <- reads cs0, (y, cs2) <- reads cs1 ] of
  [(x, y, "")] -> Just (x, y)
  _ -> Nothing

Typing

reads is ad-hoc polymorphic; which parser it chooses (Integer parser? Double parser? Maybe Integer parser?…) depends on which return type you choose, which in turn depends on context. Some cases can be inferred, and some other cases cannot be. For starters, there is no hope a standalone reads cs can have its type inferred; do not be surprised that more complex cases cannot be either.

(When in doubt, just give it a try. Add type annotations if the type checker fails.)

Examples of types choosing parsers:

reads " 34,abc" :: [(Integer,String)]
[(34,",abc")]

reads " 34e4,abc" :: [(Double,String)]
[(340000.0,",abc")]

reads " 'x',abc" :: [(Char,String)]
[('x',",abc")]

reads "Just 34,abc" :: [(Maybe Integer,String)]
[(Just 34,",abc")]

reads "(34, True),abc" :: [((Integer,Bool),String)]
[((34,True),",abc")]

Example program that can be completely inferred:

main = do
  cs <- getLine
  case reads cs of
    [(n,"")] -> print (n + length cs)
    -- due to length, n::Int is forced
    _ -> putStrLn "invalid input"

Example program that completely fails:

main = do
  cs <- getLine
  case reads cs of
    [(n,"")] -> print n
    -- not enough operations to pin down the type of n
    _ -> putStrLn "invalid input"

I have more Haskell Notes and Examples