Haskell Preludeがべき乗のために2つの別個の関数を定義している理由を誰か教えてもらえますか(つまり、^
および**
)?型システムは、この種の重複を排除するはずだと思いました。
Prelude> 2^2
4
Prelude> 4**0.5
2.0
実際には、(^)
、(^^)
、(**)
の3つの累乗演算子があります。 ^
は非負の整数べき乗、^^
は整数べき乗、**
は浮動小数点べき乗です。
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
その理由は、タイプセーフです。数値演算の結果は、通常、入力引数と同じタイプです。しかし、Int
を浮動小数点の累乗にして、Int
型の結果を取得することはできません。したがって、型システムにより、これを行うことができません。(1::Int) ** 0.5
は型エラーを生成します。 (1::Int) ^^ (-1)
についても同じことが言えます。
これを置く別の方法:Num
型は^
の下で閉じられます(それらは乗法逆数を持つ必要はありません)、Fractional
型は^^
の下で閉じられ、Floating
型は**
の下で閉じられます。 Fractional
にはInt
インスタンスがないため、負の累乗に上げることはできません。
理想的には、^
の2番目の引数は、静的に負でない値に制約されます(現在、1 ^ (-2)
はランタイム例外をスローします)。ただし、Prelude
には自然数の型はありません。
Haskellの型システムは、3つのべき乗演算子を1つとして表現するほど強力ではありません。本当に欲しいのは次のようなものです:
class Exp a b where (^) :: a -> b -> a
instance (Num a, Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a, Floating b) => Exp a b where ... -- current **
インスタンスの選択はHaskellが現在許可しているよりも賢くする必要があるため、マルチパラメータータイプのクラス拡張をオンにしても、これは実際には機能しません。
2つの演算子を定義するのではなく、3つの演算子を定義します!レポートから:
2つの引数の累乗演算が3つあります。(
^
)は任意の数を非負の整数乗に、(^^
)は小数を任意の整数乗に、そして(**
)は2つの浮動小数点引数。x^0
またはx^^0
の値は、ゼロを含むx
に対して1です。0**y
は未定義です。
つまり、3つの異なるアルゴリズムがあり、そのうち2つは正確な結果(^
と^^
)を提供し、**
はおおよその結果を提供します。使用する演算子を選択することにより、呼び出すアルゴリズムを選択します。
_^
_では、2番目の引数がIntegral
である必要があります。私が間違っていなければ、整数指数で作業していることがわかっている場合、実装はより効率的になります。また、2 ^ (1.234)
のようなものが必要な場合は、ベースが整数の2であっても、結果は明らかに分数になります。より多くのオプションがあるため、どのタイプが指数関数に出入りするかをより厳密に制御できます。
Haskellの型システムには、C、Python、LISPなどの他の型システムと同じ目標はありません。アヒルのタイピングは、(ほぼ)Haskellの考え方の反対です。