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
、を浮動小数点のべき乗に上げてtypeの結果を取得することはできません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が現在許可しているよりも賢い必要があるため、マルチパラメータータイプのクラス拡張をオンにしても、これは実際には機能しません。
Int
としInteger
ます。これら3つのインスタンス宣言を可能にするには、インスタンス解決でバックトラッキングを使用する必要があり、Haskellコンパイラはそれを実装していません。
^
2番目の引数はである必要がありIntegral
ます。私が間違っていなければ、積分指数で作業していることがわかっていれば、実装はより効率的です。また、次のようなものが必要な場合2 ^ (1.234)
基数が2であるにもかかわらず、の、結果は明らかに小数になります。より多くのオプションがあるため、指数関数でどの型が出入りするかをより厳密に制御できます。
Haskellの型システムには、C、Python、Lispなどの他の型システムと同じ目的はありません。アヒルのタイピングは、(ほぼ)Haskellの考え方とは逆です。
class Duck a where quack :: a -> Quack
アヒルに何を期待するかを定義し、各インスタンスがアヒルのように動作できるものを指定します。
Duck
。