NetworkモジュールのPortNumberというのはよく使う型だと思う。
で、環境変数からPortNumberを得たい、という用途のために
parsePortNumber :: String -> Either String PortNumber
という関数が欲しくなったのだが、PortNumberはReadのインスタンスになっていない。どうするか。
分かってしまえば簡単なのだが、PortNumberはNumのインスタンスになっている。ということはfromIntegerが適用できるではないか。
fromInteger :: Num a => Integer -> a
つまりreadでいったんIntegerにしてから、fromIntegerでPortNumberに変換すればよい。
ということで最終的には次のような実装になった。
parsePortNumber s = case readMaybe s of
Nothing -> Left ("invalid port number -> " ++ s)
Just i -> Right $ fromInteger i
たった3行のコードを書くのに二晩くらい悩んでしまった。