類型類型Num
中的數字操作全部用:: Num n => n -> n -> n
類型定義,所以操作數和返回值必須具有相同的類型。沒有辦法改變現有的類型類別,所以你的選擇是要麼定義新的操作符,要麼隱藏現有的類,並用你自己的實現來完全替代它。
爲了實現具有不同操作數類型的操作符,您將需要一些語言擴展。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
而是涵蓋+
,-
和*
一個Num
狀類的,它更靈活地定義不同類型類爲不同的操作數,因爲在Point3D * Double
有道理,Point3D + Double
通常不會。我們從Mul
開始。
class Mul a b c | a b -> c where
(|*|) :: a -> b -> c
沒有擴展,類型類永遠只含有單一類型的參數,但MultiParamTypeClasses
,我們可以聲明一個類型類像Mul
的類型a
,b
和c
組合。參數| a b -> c
之後的部分是「功能依賴性」,在這種情況下指出c
類型依賴於a
和b
。這意味着如果我們有一個像Mul Double Point3D Point3D
這樣的實例,則函數依賴關係指出我們不能有任何其他實例Mul Double Point3D c
,其中c
不是Point3D
,即乘法的返回類型始終明確地由操作數的類型決定。
下面是我們如何落實情況爲Mul
:
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)
這種靈活性並不是沒有它的警告,但是,因爲它會使類型推理編譯器了不少難度。例如,你不能簡單地寫
p = Point3D 1 2 3 |*| 5
因爲字面5
不一定Double
類型。它可以是任何Num n => n
,並且完全有可能有人聲明像Mul Point3D Int Int
這樣的新實例,其行爲完全不同。所以這意味着我們需要明確指定數字文字的類型。現在
p = Point3D 1 2 3 |*| (5 :: Double)
,如果不是定義新的操作數,我們希望從Prelude
覆蓋缺省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)
主要努力克服'Num'的問題類是[數字前奏](http://www.haskell.org/haskellwiki/Numeric_Prelude)。 – leftaroundabout 2012-02-22 15:30:10