2013-02-10 139 views
4

想象一下,我們有一個使用庫的Haskell程序。該程序從其依賴關係中爲類型T提供了一個類型類TC實例。在同一個庫的下一個版本中,庫作者爲類型T提供了另一個類型類TC的實例。相同類型的相同類型的兩個實例

我們想要使用兩個類型類實例。我們該怎麼做?

P.S.新型解決方案將無法正常工作。這兩個實例都駐留在我們無法控制的庫中。

P.P.S.我沒有真實代碼的例子。這是一個理論問題。我只想了解類型類如何與庫可組合性相結合。

回答

5

Haskell 2010 Report §4.3.2指出

  • A型可以不被聲明爲一個特定的類比在程序再次的一個實例。

所以在標準的Haskell中是不可能的。

我不知道任何GHC擴展可以讓你在GHC中做到這一點。

這是(一個?)爲什麼孤兒實例(定義與類型和類型類型不同的模塊中的實例)通常被認爲是一個壞主意。

3

我可以肯定地說,使用Cabal你只能依賴同一個庫的單一版本。

儘管您可以至少將實例替代版本的源代碼複製粘貼到項目中,但您仍然只能在每個模塊中導入其中的一個。如果您將衝突的實例導入到模塊中,您會遇到不可判定的實例問題,因此您將沒有實用的解決方案。

說實話,我無法想象爲什麼人們可能想要爲同一個庫發出的相同類型擁有不同的同一類的實例。這似乎非常不切實際。雖然我猜測什麼可以幫助你處理你的情況,但有兩個具有相關實例的類型類型:一個來自當前版本的庫,另一個來自舊版本的重命名源代碼副本。

4

一般來說,對於相同類型的多類型實例是不可能的。然而,如果一個類型是在不同的包中定義的,或者是同一個包的老版本,ghc會認爲它是一個不同的類型。所以你理論上可以用foo-1.1定義Foo及其實例,foo-1.2定義Foo及其實例,並將它們一起使用。

但是,在實踐中這不會很好。函數只能使用一種類型或另一種類型。當你編寫一個在Foo上運行的函數時,它將只在一個特定的Foo上運行,而不是兩者。基本上你有兩個完全分開的,無可爭辯的類型。這將是尷尬的使用,完全分開將是多麼尷尬,將建設。

2

與依賴關係樹上的相同包的不同版本鏈接現在不可能,因爲它導致dependency conflict

但是,這個將在GHC status update video所述的將來有可能,只要它們在同一個庫中我只能使用一個版本。

3

如果您想要更好的靈活性和可組合性,然後將類型類作爲記錄進行指定。 RankNTypes可能是必要的。

例如,這裏是你如何可以具體化應用型型類

{-# LANGUAGE RankNTypes #-} 

data ApplicativeInstance f = ApplicativeInstance 
    { pure :: forall a. a -> f a 
    , amap :: forall a b. (a -> b) -> f a -> f b 
    , ap :: forall a b. f (a -> b) -> f a -> f b 
    } 


listApplicative = ApplicativeInstance 
    { pure = \a -> [a] 
    , amap = map 
    , ap = \fs xs -> case fs of 
     [] -> [] 
     f:fs' -> map f xs ++ ap cartesianListApplicative fs' xs 
    } 


zipListApplicative = ApplicativeInstance 
    { pure = \a -> [a] 
    , amap = map 
    , ap = \fs xs -> case (fs, xs) of 
     ([], _) -> [] 
     (_, []) -> [] 
     (f:fs', x:xs') -> f x : ap zipListApplicative fs' xs' 
    } 

現在,我們獲得了我們想要的實例指定的權力。但是,我們失去了隱式選擇實例的權力:選擇現在必須是明確的。

ghci> ap listApplicative [(+1), (*3)] [1 .. 5] 
[2,3,4,5,6,3,6,9,12,15] 
ghci> ap zip 
zip     zipListApplicative zipWith3 
zip3    zipWith 
ghci> ap zipListApplicative [(+1), (*3)] [1 .. 5] 
[2,6] 

參見: http://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/