正常にパース出来た場合はJust、失敗した場合はNothingを返すmaybeRead

read関数は文字列から指定された型のデータを生成します。
型が指定されない場合、read関数はどんな型に変換すれば良いか分からないので例外を発生します。
また、パースにエラーがある場合にも、例外を発生します。

> read "123"::Int
-- > 123
-- 型が指定されない場合
> read "3.14"

<interactive>:86:1:
    No instance for (Read a0) arising from a use of `read'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read GHC.IO.Exception.ExitCode
        -- Defined in `GHC.IO.Exception'
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      ...plus 44 others
    In the expression: read "3.14"
    In an equation for `it': it = read "3.14"
-- パースにエラーがある場合
> read "abc"::Int
*** Exception: Prelude.read: no parse
  • 例外を発生させるのではなく失敗を含むMaybe型を返して欲しい

例外を補足し、正常にパース出来た場合はJust、パースに失敗した場合はNothingを返すmaybeReadの例がStack Overflowにありました。

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

Data.Time Data.Time.Calendar>  maybeRead "2000-01-01"::Maybe Day
-- > Just 2000-01-01
Data.Time Data.Time.Calendar>  maybeRead "abc"::Maybe Day
-- > Nothing
  • reads 関数が重要な役目を持っているようです。

reads 関数を少しいじってみます。

>  reads "123" ::[(Int,String)]
-- > [(123,"")]
>  reads "123 456" ::[(Int,String)]
-- > [(123," 456")]
>  reads "123abc" ::[(Int,String)]
-- > [(123,"abc")]
>  reads "2014-03-24hello,world!" ::[(Day,String)]
-- > [(2014-03-24,"hello,world!")]
  • try により Either型を返す。
Prelude Control.Exception> try (print (read "123"::Int)) ::IO (Either SomeException ())
123
-- > Right ()

Prelude Control.Exception> try (print (read "qwe"::Int)) ::IO (Either SomeException ())
-- > Left Prelude.read: no parse

Prelude Control.Exception> try (evaluate (read "123"::Int)) ::IO (Either SomeException Int)
-- > Right 123
Prelude Control.Exception> try (evaluate (read "1q23"::Int)) ::IO (Either SomeException Int)
-- > Left Prelude.read: no parse
main = do
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
    case result of
        Left what -> putStrLn $ "Division by " ++ what
        Right val -> putStrLn $ "The answer was: " ++ show val
  where
    selectDivByZero :: ArithException -> Maybe String
    selectDivByZero DivideByZero = Just "zero"
    selectDivByZero _ = Nothing