2012-04-04 50 views
5

我正在嘗試編寫一些Haskell代碼,其中有多個數據類型,每個數據類型都可以有多個實現。爲此,我將每個數據類型定義爲一個class,其方法是相關的構造函數和選擇器,然後根據給定的構造函數和選擇器對該類的成員執行所有操作。有趣的類型!解決多個實例聲明

例如,也許A是多項式類(方法getCoefficientsmakePolynomial),其可以具有表示作爲SparsePolyDensePolyB是複數類(方法getRealgetImagmakeComplex),它可以是表示爲ComplexCartesianComplexPolar

我已經複製了下面的一個簡單例子。我有兩個類AB其中每個都有一個實現。我想要將這兩個類的所有實例自動分配到Num的實例中(這需要FlexibleInstancesUndecidableInstances類型的擴展)。這工作得很好,當我只是有AB之一,但是當我嘗試用兩個編譯,我得到以下錯誤:

Duplicate instance declarations: 
    instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) => 
         Num (a x) 
    -- Defined at test.hs:13:10-56 
    instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) => 
         Num (b x) 
    -- Defined at test.hs:27:10-56 

我想,「重複實例聲明」的消息是因爲數據類型可以作爲AB的實例。我希望能夠向編譯器作出承諾,我不會那樣做,或者可能指定一個默認類以用於類型是兩個類的實例的情況。

有沒有辦法做到這一點(也許是另一種類型的擴展?)或者是我堅持使用的東西?

這裏是我的代碼:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-} 

class A a where 
    fa :: a x -> x 
    ga :: x -> a x 

data AImpl x = AImpl x deriving (Eq,Show) 

instance A AImpl where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where 
    a1 + a2 = ga (fa a1 + fa a2) 
    -- other implementations go here 


class B b where 
    fb :: b x -> x 
    gb :: x -> b x 

data BImpl x = BImpl x deriving (Eq,Show) 

instance B BImpl where 
    fb (BImpl x) = x 
    gb x = BImpl x 

instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where 
    -- implementations go here 

編輯:爲了讓自己清楚,我不是要寫使用這種技術的任何實際的代碼。我正在做這個練習來幫助我更好地理解類型系統和擴展。

+4

相關:[如何編寫,「如果類型類型a,則a也是此定義的b的一個實例。」](http://stackoverflow.com/a/3216937/98117)。 – hammar 2012-04-04 17:05:01

回答

11

你的問題的這部分

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

是不正確的。它實際上是因爲你寫的兩個實例,

instance Num (a x) 
instance Num (b x) 

編譯器無法分辨(請參閱從@哈馬爾的評論的鏈接,類上下文不計的實例聲明區分的目的)。

一個解決方案是添加一個見證類型。

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-} 

data AWitness 

data AImpl witness x = AImpl x deriving (Eq,Show) 

instance A (AImpl AWitness) where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where 
    a1 + a2 = ga (fa a1 + fa a2) 

編譯器可以使用證人類型來區分您的實例聲明。

+0

謝謝,這是我走的方法。 – 2012-04-05 07:42:24

4

有沒有真正的好辦法做到這一點;最好的做法是這樣定義

plusA, minusA :: (A a, Num x) => a x -> a x -> a x 

這使得編寫Num情況下,更多的機械您有A實例之後,一些常量:

instance A Foo where ... 
instance Num x => Num (Foo x) where 
    (+) = plusA 
    (-) = minusA 
+0

謝謝!我可以看到,如果每個類只有少量的實現,這將會很有用。 – 2012-04-05 07:43:10