2013-08-26 48 views
9

我正在開發一個專門的數值數據處理庫,並且遇到了一個我無法弄清楚如何解決的錯誤。我認爲首先展示一個例子並解釋我的問題會更容易。我也爲奇怪的名字表示歉意,爲了合法目的我不得不混淆視聽。解決多參數類型類的模糊實例

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances  #-} 

data MyError = MyError String deriving (Eq, Show) 

data MyList = MyList [Double] deriving (Eq, Show) 
data NamedList = NamedList String MyList deriving (Eq, Show) 

class MyNum a b ret where 
    myAdd  :: a -> b -> Either MyError ret 
    myLessThan :: a -> b -> Either MyError Bool 

instance MyNum MyList Double MyList where 
    myAdd (MyList xs) x = Right $ MyList $ map (+x) xs 
    myLessThan (MyList xs) x = Right $ all (< x) xs 

instance MyNum NamedList Double NamedList where 
    myAdd (NamedList n l) x = fmap (NamedList n) $ myAdd l x 
    myLessThan (NamedList n l) x = myLessThan l x 

如果我嘗試編譯,我得到了錯誤

No instance for (MyNum MyList Double ret0) 
    arising from a use of `myLessThan' 
The type variable `ret0' is ambiguous 
Possible fix: add a type signature that fixes these type variable(s) 
Note: there is a potential instance available: 
    instance MyNum MyList Double MyList 
    -- Defined at testing_instances.hs:13:10 
Possible fix: 
    add an instance declaration for (MyNum MyList Double ret0) 
In the expression: myLessThan l x 
In an equation for `myLessThan': 
    myLessThan (NamedList n l) x = myLessThan l x 
In the instance declaration for `MyNum NamedList Double NamedList' 

因爲編譯器無法弄清楚用於MyList其中MyNum具體實例。它適用於myAdd,因爲MyNum的返回類型很容易推導出來,但無法找出myLessThan。我想使用這個類型類型,以便我可以輕鬆地添加細粒度的錯誤處理,並且因爲我的實際代碼具有等效於+, - ,*,/,<,< =,>和> =,我想要爲MyNum Double MyList MyListMyNum MyList MyList MyListNamedList做出類似的實例。除非有更簡單的方法來做到這一點,否則我可以擁有多態交換操作符。

但是,我不知道什麼類型的簽名添加到第二個實例myLessThan,以便它可以知道使用哪個實例。我知道一種解決方案是將算術和比較運算符分爲兩個單獨的類型類,但是我想盡可能避免這樣做。

回答

11

您可以使用functional dependencies來指定「retab唯一確定」。

... 
{-# LANGUAGE FunctionalDependencies #-} 
... 
class MyNum a b ret | a b -> ret where 
... 

這讓typechecker知道,它可以選擇正確的實例定義,只知道從參數中ab您:

myLessThan (NamedList n l) x = myLessThan l x 

,如果你定義一個額外的編譯器現在抱怨實例具有相同ab但不同ret,如

instance MyNum MyList Double SomeOtherType where 
+0

這是Haskell以前對我而言不熟悉的構造,正是我所需要的,謝謝! – bheklilr

+2

@bheklilr很高興我能幫到你。您可能還想了解TypeFamilies,它們在某些方面更易於使用,功能更強大。 – jberryman

4

正如jberryman指出的,你可以使用TypeFamilies。這該是多麼:

-{-# LANGUAGE FlexibleInstances  #-} 
+{-# LANGUAGE TypeFamilies #-} 

-class MyNum a b ret where 
- myAdd  :: a -> b -> Either MyError ret 
+class MyNum a b where 
+ type Ret a b 
+ myAdd  :: a -> b -> Either MyError (Ret a b) 

-instance MyNum MyList Double MyList where 
+instance MyNum MyList Double where 
+ type Ret MyList Double = MyList 

-instance MyNum NamedList Double NamedList where 
+instance MyNum NamedList Double where 
+ type Ret NamedList Double = NamedList 

我剛剛搬到從類參數ret類型associated typeRet
這是一個TypeFamily - 通過說明從類參數abRet有功能。

+0

我喜歡它從MyNum類中移除第三個顯式類型參數。但是有沒有人有任何關於哪一個性能更好的信息?如果它意味着運行速度更快,或者類型族效率更高,我不介意更多混亂的代碼,我寧願使用該解決方案。 – bheklilr

+1

據我所知,兩種解決方案都有相同的性能。你可以檢查編譯的核心,看看是否有任何不同 - 但在這種情況下,我不希望有一個。 – ocharles

+0

@ocharles當我有機會的時候,我會嘗試分析它,然後我會嘗試稍後發佈更新以及結果 – bheklilr

相關問題