最初はreadTextみたいな関数を作ろうとしていました。
readの、StringじゃなくてTextを取る版。
readText :: Read a => Text -> a
で、最初はこんな感じで実装しようとしていた。
module Main where import Data.Text (Text) import qualified Data.Text as T readText :: Read a => Text -> a readText = read . T.unpack
unpackしてできたStringをreadに食わせれば終わりじゃないか。
と思っていたんですが、
ghciで試してみると
Main> :set -XOverloadedStrings Main> :m Data.Text Main Data.Text> readText "193" :: Int 193 Main Data.Text> readText "abide" :: Text "*** Exception: Prelude.read: no parse
TextをTextに変換しようとすると失敗する。
「TextってReadのインスタンスだよね? なんで?」って一人で大騒ぎしていました。
というかよくよく考えて試してみると、
Prelude> read "841" :: Int 841 Prelude> read "abc" :: String "*** Exception: Prelude.read: no parse
Stringでもunpackとかしなくても普通に失敗する。Intはうまくいくのに。
ヒントはもう出ていて、`"*** Exception:`のところの最初のダブルクォーテーション、文字列をshowした時に出るやつで、文字列はshowしたりする時はダブルクォーテーションで囲まれる。
同様に、readに渡す文字列は文字列の文字列表現の形をとっている必要があって、
つまり
Prelude> read "\"abc\"" :: String "abc"
文字列は文字列らしくダブルクォーテーションで囲めばよろしい。
文字列だと思ってparseしようとしたのにダブルクォーテーションが無かったから困ったねって事になっていたようです。
ということで、最初のreadTextの実装を変更してみる。unpackじゃなくてshowしてやればいいじゃないか。
readText :: Read a => Text -> a readText = read . show
showしてreadってなんかなんじゃそりゃって感じですけどね。
でもこれはこれで問題があって、
Main> readText "186" :: Int *** Exception: Prelude.read: no parse
今度は数字が読めなくなっている。全然駄目だ。
showしたことで文字列がダブルクォーテーションで囲まれてしまって、数字としてreadできなくなったということで。
そういう時だけは(read . unpack)でいいんだけど。
で、最終的にSafeモジュールのreadMayを使ってこうなった。
import Data.Text (Text) import qualified Data.Text as T import Safe (readMay) import Data.Maybe (fromMaybe) readText :: Read a => Text -> a readText t = fromMaybe (read $ T.unpack t) . readMay . show $ t
文字列系に変換するパターンの方が圧倒的に多かったのでこういう感じに。文字列以外に変換するパターンが多かったら、showとunpackの位置が逆になるんじゃないでしょうか。
ということで出来上がったのはreadではなくて、なんか、汎用のデータ変換関数? 結局readとは少し違うものができてしまいましたし、最初から目指していたのはこれでした。
この変換関数を作成中のプロダクトの基盤部分に突っ込んでみた結果、それはそれで「型推論できないぞ」って怒られまくったんですが、それはまた別の話。
readを使いこなすのって難しいですよねぇ。というかあんまし使いたくないけども。