2014-12-05 28 views
10

因此,有C a Bool形式的類型類有許多優點。主要是因爲當正常的C a只是隱含地AND與一切有關時,它們讓你在兩個約束之間做任何邏輯運算。將任意類約束`C a`轉換爲`C a Bool`

如果我們考慮~一類約束,這可以像這樣

class Equal x y b | x y -> b 
instance Equal x x True 
instance False ~ b => Equal x y b 

但是是什麼讓這種情況下,特別是,把x x在實例的頭部相當於x ~ y =>然後x y完成在頭上。對於其他類型類別,情況並非如此。 所以,如果我們試圖做類似的事情一類C我們得到類似

class C' x b | x -> b 
instance C x => C' x True 
instance False ~ Bool => C' x b 

不幸的是,這並不因爲只有其中的一個實例的工作將永遠不會回升,因爲他們不類型x歧視所以任何類型都匹配兩個頭。

我也讀過https://www.haskell.org/haskellwiki/GHC/AdvancedOverlap,它再次不適用於任何類C,因爲它要求您重寫原始類的所有實例。理想情況下,我希望我的代碼與GHC.Exts.ConstraintKindSignatures一起使用,以便C可以是參數化的。

所以,像這樣一類

class Match (c :: * -> Constraint) x b | c x -> b 

我怎樣寫的實例,以便Match c x True當且僅當c xMatch c x False否則?

+0

的'C一Bool'形式比'C了'更強。你實質上是在問如何收集類型類的一組實例,這是不可能的。 – user2407038 2014-12-05 13:13:51

+0

@ user2407038不是我懷疑你,而是我之前聽說過類型系統是'不可能的',結果是錯誤的。 – 2014-12-05 13:34:15

+1

我不知道我是否可以提出令人信服的論點,但我會嘗試。假設你寫了'Match'。在模塊A中,我定義了'數據X = X',並且'類A b x | b - > x; a :: Proxy b - > x;實例一個True Int;實例一個虛假的Bool','test :: forall x b y。 (匹配等式x b,A b y)=> x - > y; test _ = a(Proxy :: Proxy b)'。我有模塊B(進口A),其中'test X'的類型必須是'Int'。在模塊C(導入A)中,我有'實例Eq X',所以'測試X :: Bool'。模塊D導入B和C.模塊D不能強制B和C重新編譯,所以'test X'必須一次反常地有兩種類型。 – user2407038 2014-12-05 19:18:35

回答

1

由於所謂的開放世界假設,這在Haskell中是不可能的。它指出,類型類的實例集是開放的,這意味着你可以隨時創建新的實例(而不是封閉的世界,必須有一組固定的實例)。例如,在前奏中定義Functor類型類別時,我仍然可以在我自己的代碼中爲它創建實例,但它不在Prelude中。

爲了實現您的建議,編譯器需要一種方法來檢查類型T是否爲C類的實例。然而,這要求編譯器知道該類的所有可能實例,並且由於開放世界的假設,這是不可能的(編譯Prelude時,編譯器還不知道您以後也會使爲Functor的實例)。

使其工作的唯一方法是僅考慮當前可見的實例(因爲它們是在當前模塊或其任何導入中定義的)。但是這會導致相當不可預知的行爲:一組可見實例不僅取決於模塊的導入,還取決於導入的導入,因爲您無法隱藏實例。所以你的代碼的行爲將取決於你的依賴關係的實現細節。

如果您想要一個封閉的世界,您可以改爲使用GHC 7.8中引入的closed type families。使用它們,你可以這樣寫:

type family Equal a b :: Bool where 
    Equal x x = True 
    Equal x y = False 
相關問題