2015-03-13 67 views
7

我正在寫一個類似於應用程序的CRUD,並且有很多通過主鍵查找(主鍵可以有不同類型)。所以我定義以下類型類:推測公式類型

{-# LANGUAGE MultiParamTypeClasses #-} 

class Eq b => HasPK a b where 
    getPK :: a -> b 

現在我可以這樣寫:

import Data.Maybe 

lookupPK :: HasPK a b => b -> [a] -> Maybe a 
lookupPK s = listToMaybe . filter ((== s) . getPK) 

現在,當我想比較PK兩件事情,我只是想比較它們的PK的。所以,我想這個定義:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 

instance (HasPK a b) => Eq a where 
    (==) = (==) `on` getPK 

但現在它給了我:

src/Utils.hs:61:10: Could not deduce (HasPK a b0) … 
     arising from the ambiguity check for an instance declaration 
    from the context (HasPK a b) 
     bound by an instance declaration: HasPK a b => Eq a 
     at /home/utdemir/workspace/.../Utils.hs:61:10-28 
    The type variable ‘b0’ is ambiguous 
    In the ambiguity check for: forall a b. HasPK a b => Eq a 
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes 
    In the instance declaration for ‘Eq a’ 
Compilation failed. 

任何人都可以解釋這個錯誤給我嗎?我在正確的軌道上,還是有更安全的方式來實現我想要的?

回答

9

這裏您需要的功能依賴:使用

class Eq b => HasPK a b | a -> b where 
    getPK :: a -> b 

,使任何分機GHC點。

問題在於具有用於同一類型的多個的PK的可能性, 如

instance HasPK MyType Int where ... 
instance HasPK MyType String where ... 

由於上述雙實例可以被添加後,像(==) `on` getPK代碼是可能具有不同含義。實際上,當添加上面的實例時,它不指定是使用Int PK還是使用String


然而,像

instance (HasPK a b) => Eq a where 
    ... 

一個實例可能會導致問題,因爲它與任何其他Eq實例重疊。在這裏小心點。我反而寫

equalPK :: HasPK a b => a -> a -> Bool 
equalPK = (==) `on` getPK 

,然後提供給所有相關類型明確的情況下,因爲這:

instance Eq MyType1 where (==) = equalPK 
instance Eq MyType2 where (==) = equalPK 
... 

作爲另一種選擇,你可以使用一種類型的家庭,如

class HasPK a where 
    type PK a 
    getPK :: a -> PK a 

equalPK :: Eq (PK a) => a -> a -> Bool 
equalPK = (==) `on` getPK 

instance Eq MyType1 where (==) = equalPK 
... 
+0

是的,在定義了這個實例後,我得到了'src/Utils.hs:52:20:因使用'/ ='而產生的Eq整數的重疊實例... 匹配實例: 實例Eq整數 - 定義在'integer-gmp:GHC.Integer.Type'實例(等式B,HasPK a b)=>等式a'中。但是由於'Integer'沒有'HasPk Integer b'實例,所以我並不期待這個錯誤。如果我定義了Eq和HasPK,我會理解這個錯誤,但是由於沒有'HasPK Integer',它不應該直接使用普通的'Eq'實例嗎? – utdemir 2015-03-13 09:36:37

+2

@utdemir問題是:一個'實例C a => Eq a'適用於每種類型,即使對於那些C a'爲假(!)的類型也適用。 Haskell將承諾使用此實例,並且當'C a'時,它將會出錯,而不是回溯並查找其他實例。這樣做是因爲回溯問題變得更加困難,而Haskell設計人員則關注編譯時間。GHC有一個'OverlappingInstances'擴展,可以放寬這個限制,但我不推薦它。重疊的實例最好避免,恕我直言。 – chi 2015-03-13 09:48:36

+0

謝謝,我選擇了家庭解決方案,因爲它看起來像是一個最沒有爭議的擴展。 – utdemir 2015-03-13 15:16:29