Num
の数値演算はすべてタイプが:: Num n => n -> n -> n
で定義されているため、両方のオペランドと戻り値の型が同じでなければなりません。既存の型クラスを変更する方法はないので、新しい演算子を定義するか、または既存のNum
クラスを非表示にして、独自の実装と完全に置き換えることができます。
異なるオペランドタイプを持つ演算子を実装するには、いくつかの言語拡張が必要です。その代わり+
、-
と*
を含むNum
様クラスの
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
Point3D * Double
は理にかなっている間、Point3D + Double
は通常ないので、それは、異なるオペランドのためのさまざまな型クラスを定義するために、より柔軟です。 Mul
から始めましょう。拡張子なし
class Mul a b c | a b -> c where
(|*|) :: a -> b -> c
、型クラスは今まで、単一の型パラメータを含むが、MultiParamTypeClasses
で、我々はタイプa
の組み合わせについてMul
、b
とc
などの型クラスを宣言することができます。パラメータの後の部分、| a b -> c
は、この場合、タイプc
がa
とb
に依存していることを示す「機能的依存」です。これは、Mul Double Point3D Point3D
のようなインスタンスを持つ場合、他のインスタンスを持つことができないということを意味します。Mul Double Point3D c
Point3D
以外のもの、つまり、戻り値の型は常にオペランドの型によって明白に決まります。
instance Mul Double Double Double where
(|*|) = (*)
instance Mul Point3D Double Point3D where
Point3D x y z |*| a = Point3D (x*a) (y*a) (z*a)
instance Mul Double Point3D Point3D where
a |*| Point3D x y z = Point3D (x*a) (y*a) (z*a)
を、それはコンパイラの型推論は、より多くの困難になりますので、この柔軟性は、しかし、その警告なしに来ない:ここでは
たちはMul
のインスタンスを実装する方法を説明します。リテラル5
は必ずしもタイプDouble
のではありませんのでたとえば、あなたは、単に
p = Point3D 1 2 3 |*| 5
を書き込むことはできません。任意のNum n => n
とすることができ、誰かが完全に異なる動作をするMul Point3D Int Int
のような新しいインスタンスを宣言することは完全に可能です。つまり、数値リテラルの型を明示的に指定する必要があるということです。代わりに新しいオペランドを定義するので我々はPrelude
からデフォルトNum
クラスをオーバーライドしたい場合
p = Point3D 1 2 3 |*| (5 :: Double)
今、私たちは `Num`の問題を克服するために、この
import Prelude hiding (Num(..))
import qualified Prelude as P
class Mul a b c | a b -> c where
(*) :: a -> b -> c
instance Mul Double Double Double where
(*) = (P.*)
instance Mul Point3D Double Point3D where
Point3D x y z * a = Point3D (x*a) (y*a) (z*a)
のような主要な努力を行うことができますclassは[Numeric Prelude](http://www.haskell.org/haskellwiki/Numeric_Prelude)です。 – leftaroundabout