処理系がデフォルトの型を使ったという警告です
どうして型が決定できないか
読みやすさのために、質問者のコードを再掲します。
haskell
1{-# OPTIONS -Wall -Werror #-}
2
3square' :: Double -> Double
4square' x = x ^ 2
このエラーは数値リテラル2の型が決定できないために、生じています。
Haskellでは数値リテラルの型は多相性を持ちます。
対話環境で、つぎのように試してみてください。
haskell
1> 2 :: Integer
22
3> :type it
4it :: Integer
5> 2 :: Double
62.0
7> :type it
8it :: Double
9> :type 2
102 :: Num t => t
数値リテラル2は多相的であり、実際に計算が行われるときには、Numクラスに含まれる、どれかの型に決定されます。
さて、演算子(^)の型を見てみましょう。
haskell
1(^) :: (Num a, Integral b) => a -> b -> a
第1引数はNumクラスに含まれる型の値、第2引数はIntegralクラスに含まれる型の値、返り値は第1引数と同じ型の値、となります。
ここで、問題の式を見てみましょう。
リテラル2はNumクラスである型の値となります。また、演算子(^)を適用していることから、よりきびしく、Integralクラスの型の値であることを要求されます。しかし、それだけです。これだけでは数値2の型が定まりません。
型の決まらない「整数値」のデフォルトはInteger
ここで、Haskellには、型の決まらない「整数値」をデフォルトでIntegerとする、というルールがあります。そのルールで数値2の型はIntegerとなります。
しかし、この「デフォルトの型」はコードを書いた人の意図とは異なるかもしれません。そこで警告が出ます。
解決の方法の、いくつかを挙げます。
まず、質問者が -Wall -Werror としているのは大変良い習慣かと思います。
そこで、それを生かす方向で、いくつかの解決策を挙げておきます。
デフォルトの型を使うことへの警告をつぶす
コードを書く人が、Haskellがデフォルトとして選ぶ型を信頼しているなら、つぎのようにします。
haskell
1{-# OPTIONS -Wall -fno-warn-type-defaults -Werror #-}
こうすると、Haskellがデフォルトの型を選択したときに、警告しなくなります。
型注釈をつける
より、おすすめの方法は、型注釈をつけることです。
haskell
1square' x = x ^ (2 :: Integer)
このようにすれば、明示的に数値2の型をIntegerと特定することができます。
Int型の値のほうが、すこしだけ、空間効率が良い気がするので、つぎのようにするのも、いいかもしれません。
haskell
1square' x = x ^ (2 :: Int)
演算子(**), (^), (^^)について
それぞれの型を再掲します。
haskell
1(**) :: Floating a => a -> a -> a
2(^) :: (Num a, Integral b) => a -> b -> a
3(^^) :: (Fractional a, Integral b) => a -> b -> a
実は、型だけからでも、あるていど、これらのちがいは予想できます。
演算子(**)では、冪乗する数である第2引数はFloatingクラスの型の値です。
つまり、浮動小数点っぽい値だということです。
整数でなくてもいい、つまり、つぎのようなことができます。
haskell
1> 3 ** 0.5
21.7320508075688772
それに対して、演算子(^)では、第2引数は整数値です。
つまり、x ^ yは、「xをy回、かけあわせたもの」です。
このとき、xはNumクラスのインスタンスである型の値なら、何でもいいです。
Numクラスのクラス関数に「かけ算」があるためです。
最後の(^^)については、第1引数がFractionalクラスの型、つまり、分数(小数)となっています。整数では、負の数での冪乗ができないためです。
haskell
1> 2 ^ (- 1)
2Exception: Negative exponent
3> 2 ^^ (- 1)
40.5
どうして、(**)で解決したか
演算子(**)の型は、つぎのようになっています。
haskell
1(**) :: Floating a => a -> a -> a
質問者が新しく定義した関数は、つぎのようになります。
haskell
1square' :: Double -> Double
2square' x = x ** 2
演算子(**)は、第1引数、第2引数、返り値のすべてが同じ型であることを、要求しています。
そしてsquare'では引数と返り値とがDouble型の値であることを、要求します。
これらから、数値2の型がDouble型に決定されます。
ただ、この解決は、あまり好ましくないと感じます。
「ある数を、1.5乗でもなく2.5乗でもなく2乗したい」という意図であれば、
上記の解決策がぴったりですが、
「ある数を、2回かけあわせたい」という意図であれば、演算子(^)が、ぴったりかと思います。
そして、第2引数の数値リテラルには、好みの整数型を指定してあげるのが良いと思われます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/05/30 03:03
2017/05/30 08:00