floorとsqrtにはまった話
型クラスを理解してなくてはまってしまった.
nums n = [1..floor $ sqrt ( n / 2 )]
上記の関数に型をつけようとして,まず初めに
nums :: Integral a => a -> [a]
と書いた.今まで
nums n = [1..n]
に型をつける場合,Integralを指定してたからですが,やっぱり無理でした.
そこで,次に
nums :: Fractional a => a -> [a]
と書きました.当然だめでした.なにも考えてないですね.私は...orz
で,sqrtができないと通るはずがないので,
nums :: Floating a => a -> [a]
と書きました.だめです.やっぱりなにも考えてないですね.私は...orz
思ったとおりですが,今度はfloorを考えて
nums :: RealFrac a => a -> [a]
だめですよね.ここで,やっと適当に型クラスを指定し続けても,この問題が解消されないことに気づきました.
そこでまず,出力の型を別にしないといけないことに気付きました.
nums :: (RealFrac a, Integral b) => a -> [b]
エラーの数が減りました.今度はsqrt とか floor とかのクラス構造はどうなってるのか疑問になり調べてみました.
ちょうどいい画像がありました.
- ref:The Haskell 98 Report: 定義ずみの型とクラス
- ref:
なんと複雑なんだとおもいつつ,
(/) は Fractional.
sqrt は Floating.
floor は RealFrac.
なので,あれしかありません.
nums :: (RealFloat a, Integral b) => a -> [b]
と,してみたら怒られなくなりました.
これで安心して,眠りについたのですが,ふとあれは正解ではない気がしました.
次の日に書いたのが,下の奴です.
nums :: (Floating a, RealFrac a, Integral b) => a -> [b]
やってみたら,思ったとおりコンパイラに文句を言われませんでした.
FloatingとRealFracの両方を満たすのは,RealFloatしかないと(Java的発想?)勘違いしてたのですが,1つの変数に対して2つ以上の型クラスを指定することができるのでこう書くほうが良いはずです.
HaskellはFloat, Double, Integer, Intあたりのクラス構造が複雑すぎです.と,理解の低さを棚に上げて吼えてみました.サーセン.