reads
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.
The principle of using reads
is exemplified by the following
cases:
reads "" :: [(Integer,String)] reads "abc34" :: [(Integer,String)]
The result is []
. This stands for parse errors.
reads "34" :: [(Integer,String)] reads " 34" :: [(Integer,String)]
The result is [(34, "")]
. This stands for successful parses,
and you get the number. Note that leading spaces are skipped first. (What about
trailing spaces? See the next case.)
reads "34 " :: [(Integer,String)] reads " 34 " :: [(Integer,String)]
The result is [(34, " ")]
. Trailing data
is isolated and served, in case you want to check it, or it contains other
things you want to parse out.
reads "34abc" :: [(Integer,String)] reads " 34abc" :: [(Integer,String)]
The result is [(34, "abc")]
.
There is a 4th case of getting a list with 2 or more tuples. This
does not happen with the default parsers. But you can make it happen when
you write your own Read
instance, if you want to support
an ambiguous grammar, and so there can be multiple correct parses and you
give/get them all in a huge list.
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
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