2015-12-26 63 views
3

我有以下多PARAM類型類與函數依賴於它們是(載體)混淆類型推斷和函數依賴

module Vec where 

class Vec v k | v -> k where -- v is an element of a vector space over k 
    vZero :: v    -- The zero vector in v 
    vAdd :: v -> v -> v  -- Adds two vectors 
    vSub :: v -> v -> v  -- Subtracts two vectors 
    vMul :: v -> k -> v  -- Multiplies a vector by a number from k 

infixl 6 |+| -- Shortcut operator for accessing vAdd 
(|+|) :: Vec v k => v -> v -> v 
(|+|) = vAdd 

現在向量空間的元素類型的I加載的代碼段上方插入到ghci解釋,並要求它向我展示運營商|+|類型:

*Vec> :t (|+|) 
(|+|) :: Vec v k => v -> v -> v 

到現在爲止,一切似乎都很正常。但現在我要指定所有數字都是一個特殊的向量空間的元素在自己:

instance Num k => Vec k k where 
    vZero = 0 
    vAdd = (+) 
    vSub = (-) 
    vMul = (*) 

現在奇怪的事情發生了:ghci不再顯示正確的類型|+|(儘管它是明確由我指定在上面的代碼):

*Vec> :t (|+|) 
(|+|) :: Num v => v -> v -> v 

我懷疑這種奇怪的行爲與我使用的FunctionalDependencies語言的擴展聯繫起來,但我不明白爲什麼ghc行爲這種方式。我可以看到我自己添加了一個不同的實例Vec v k其中v而不是Num的一個實例,因此這樣的實例不會與現有實例重疊,從而保留了函數依賴關係。

回答

4

您已定義非常一般的實例:Vec v ...。 沒有重疊的情況下,不能有其他的。

E.g.加入

data V2 k = V2 k k 
instance Num k => Vec (V2 k) k where 

成果轉化

Functional dependencies conflict between instance declarations: 
    instance Num k => Vec k k -- Defined at v.hs:15:10 
    instance Num k => Vec (V2 k) k -- Defined at v.hs:23:10 

其實重疊的情況下,不幫助這裏是(這可能是一個GHC誤功能?)。

如果您嘗試編碼此使用TypeFamilies你會得到類似的錯誤:由

Conflicting family instance declarations: 
    Elem (V2 k) -- Defined at v.hs:20:10 
    Elem k -- Defined at v.hs:23:10 

解決方案將是確定的輔助函數,寫實例定義」:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE UndecidableInstances #-} 
module Vec where 

class Vec v where -- v is an element of a vector space over k 
    type Elem v 
    vZero :: v     -- The zero vector in v 
    vAdd :: v -> v -> v  -- Adds two vectors 
    vSub :: v -> v -> v  -- Subtracts two vectors 
    vMul :: v -> Elem v -> v -- Multiplies a vector by a number from k 

infixl 6 |+| -- Shortcut operator for accessing vAdd 
(|+|) :: Vec v k => v -> v -> v 
(|+|) = vAdd 

data V2 k = V2 k k 

instance Num k => Vec (V2 k) where 
    type Elem (V2 k) = k 
    -- implementation omitted 

instance Num k => Vec k where 
    type Elem k = k 
    vZero = 0 
    vAdd = (+) 
    vSub = (-) 
    vMul = (*) 

錯誤與手「:

{-# LANGUAGE FlexibleInstances, FunctionalDependencies #-} 
module Vec where 

class Vec v k | v -> k where -- v is an element of a vector space over k 
    vZero :: v    -- The zero vector in v 
    vAdd :: v -> v -> v  -- Adds two vectors 
    vSub :: v -> v -> v  -- Subtracts two vectors 
    vMul :: v -> k -> v  -- Multiplies a vector by a number from k 

infixl 6 |+| -- Shortcut operator for accessing vAdd 
(|+|) :: Vec v k => v -> v -> v 
(|+|) = vAdd 

-- The definitions are so short, that it's not worth even givin them names 
numVZero :: Num k => k 
numVZero = 0 

instance Vec Int Int where 
    vZero = 0 
    vAdd = (+) 
    vSub = (-) 
    vMul = (*) 

data V2 k = V2 k k 
instance Num k => Vec (V2 k) k where 
    vZero = V2 0 0 
    vAdd (V2 a b) (V2 c d) = V2 (a + c) (b + d) 
    vSub (V2 a b) (V2 c d) = V2 (a - c) (b - d) 
    vMul (V2 a b) k  = V2 (a * k) (b * k) 

然後:

λ *Vec > :t (|+|) 
(|+|) :: Vec v k => v -> v -> v 

或者爲Num v => Vec v v很可能將是相當常見的,你可以使用DefaultSignatures 減少實例聲明的樣板:

{-# LANGUAGE GADTs, FlexibleInstances, FunctionalDependencies, DefaultSignatures #-} 
module Vec where 

class Vec v k | v -> k where -- v is an element of a vector space over k 
    vZero :: v    -- The zero vector in v 
    default vZero :: (Num v, v ~ k) => v 
    vZero = 0 

    vAdd :: v -> v -> v  -- Adds two vectors 
    default vAdd :: (Num v, v ~ k) => v -> v -> v 
    vAdd = (+) 

    vSub :: v -> v -> v  -- Subtracts two vectors 
    default vSub :: (Num v, v ~ k) => v -> v -> v 
    vSub = (-) 

    vMul :: v -> k -> v  -- Multiplies a vector by a number from k 
    default vMul :: (Num v, v ~ k) => v -> k -> v 
    vMul = (*) 

infixl 6 |+| -- Shortcut operator for accessing vAdd 
(|+|) :: Vec v k => v -> v -> v 
(|+|) = vAdd 

instance Vec Int Int 
instance Vec Integer Integer 
instance Vec Float Float 
instance Vec Double Double 

data V2 k = V2 k k 
instance Num k => Vec (V2 k) k where 
    vZero = V2 0 0 
    vAdd (V2 a b) (V2 c d) = V2 (a + c) (b + d) 
    vSub (V2 a b) (V2 c d) = V2 (a - c) (b - d) 
    vMul (V2 a b) k  = V2 (a * k) (b * k) 
+2

您可以節省一點的樣板使用[DefaultSignatures](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/type-class-extensions。html#class-default-signatures),並將默認類型定義爲,例如,默認vAdd ::(Num k,v_k)=> k-> k-> k; vAdd =(+)'。然後你可以簡單地寫'instance Vec Int Int;實例Vec Float Float;實例Vec Integer Integer'等,省略了實例的主體。 – user2407038

+0

我不知道是否可以付費使用簡單的新型面膜。 'newtype Self a = Self a'表示數字類型的矢量場透視圖,所以你可以讓'instance Num a => Vec(Self a)a where ...'。 – dfeuer