[Haskell-cafe] [Newbie question] -- Looping stdin until condition is met

Don Stewart dons at galois.com
Fri May 30 19:58:28 EDT 2008

> Allright, this is a definitely a newbie question.
> I'm learning Haskell and going through the exercises in the
> beautiful Hutton book, and one of them requires for me to
> write a loop that queries a line from the user (stdin),
> looping until the user enters a valid integer (at least
> that's how I want to implement the interface to the
> exercise).
> 1. How do I catch the exception that is raised from "read"?

The best thing to do is bypass read and use 'reads' to define your
own safe read.

    maybeRead :: Read a => String -> Maybe a
    maybeRead s = case reads s of
        [(x, "")] -> Just x
        _         -> Nothing

For example, yielding:

    import System.IO

    main = do
        x <- getNum
        print x

    getNum :: IO Integer
    getNum = do
        n <- getLine
        case maybeRead n of
            Nothing -> getNum
            Just n  -> return n

> 2. Where do I find the appropriate information I need in
>    order to fix this? I'm probably just not searching in the
>    right place. (Yes, I've seen the GHC docs, and it doesn't
>    help, maybe I'm missing some background info.)

I think Control.Exception.catch should be fine here.
> 3. Please do not tell me I should solve the problem
>    differently. Here is the problem I'm trying to solve, and
>    nothing else:
>      "Write a program that reads a line from the user,
>      looping the query until the line contains a valid
>      integer."
> It shouldn't be too hard i think. The best answer would be a
> two-liner code example that'll make me feel even more stupid
> than I already do.

Of course, it's easy. You can have fun now abstracting out the loop
form in getNum using say, MaybeT or friends. But a loop is simple and easy.

If you want to write it with explict exception handling of the
read parse failure, it's more tedious, as you need to ensure
the read exception is thrown within the body of the enclosing catch.

For example,

    import System.IO
    import qualified Control.Exception as C

    main = do
        x <- getNum
        print x

    getNum :: IO Integer
    getNum = do
        y <- maybeRead
        case y of
            Nothing -> getNum
            Just n  -> return n

    maybeRead :: Read a => IO (Maybe a)
    maybeRead = C.catch
        (do x <- getLine
            let n = read x
            n `seq` return (Just n)) -- ensure any exception is thrown here
        (const (return Nothing))

-- Don

More information about the Haskell-Cafe mailing list