read' :: String -> Maybe a

Maybe aを返すread'を作ってみた。エラーを吐く代わりにNothingを返すreadのバリエーション。
発端はHaskell学習中の某Tくんが書いた、数値な文字列を数字に変換するコード。結局は文字列をreadに食わせていたのだが、readは文字が解析できない場合は問答無用でエラーを吐く。なので、T君はその前処理としてreadがエラーを吐かないようにがんばって文字列をチェックしてた。空文字列をはじいたり、数字以外の文字をはじいたり。
その涙ぐましい努力に「文字列をノーチェックでreadにくわせて、エラー補足したほうが早いんちゃうの」と思ったのだが、エラーを(いわゆる例外みたいに)補足する方法がさっぱりわからなかったので、駄目な時はNothingを返すMaybe版をつくってみた。

まずreadのソースを見てみる。ひよってぐぐってみると、readはプレリュードでこう定義されているらしい(http://www.sampou.org/haskell/report-revised-j/basic.htmlより)。

read    :: (Read a) => String -> a
read s  =  case [x | (x,t) <- reads s, ("","") <- lex t] of
              [x] -> x
              []  -> error "PreludeText.read: no parse"
              _   -> error "PreludeText.read: ambiguous parse"

内容はいまいちわからないが、エラーを出してるのは最後の二行のerror。最初はこいつを外側から補足してやろうと思ったのだが、GHCiで:t errorしてみると"error :: forall a. [Char] -> a"などという味もそっけもない型が出力される。モナドでもなんでもないし、traceと同様の裏道関数くさい。この時点で正面から立ち向かうのはあきらめた。
で、仕方ないので、変換できる時は値を返して欲しいが駄目な時はメッセージなしのNothingだけでいいやと割りきって、Maybeを使うコードに愚直に書きかえた。これ。

read'    :: (Read a) => String -> Maybe a
read' s  =  case [x | (x,t) <- reads s, ("","") <- lex t] of
              [x] -> Just x
              _   -> Nothing

それなりにちゃんと動くようだ。Maybe付きで欲しい型を指定してやればちゃんと拾える。こんなかんじ。

*Main> read' "100"
Just 100
*Main> read' ""
Nothing
*Main> read' "1.0"
Nothing
*Main> read' "1.0" :: Maybe Float
Just 1.0
*Main> read' "True"
Nothing
*Main> read' "True" :: Maybe Bool
Just True

どっかに似たような関数は既にちゃんとあるんだろうな。それとも、みんなエラーを吐くreadで満足してんのかな?エラーを補足する方法があるっていうのが一番ありそうか。うーむ。