2016-08-30 91 views
2

我目前正在一個項目中爲一個類派生一些實例。由於類只有一個方法,除了一些特定的情況外,它們將具有相同的定義,所以我嘗試定義一個可重疊的一般實例,然後定義我需要重疊的實例。重疊實例的問題

這不起作用,因爲我得到重疊的實例錯誤。做一些測試,我們來到翻過這個減少的例子,這相當於我原來的問題相當多:

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

module Instance where 

data Id a = Id a String 

data C a = C a 

class Bad a b where 
    bad :: a -> String 

instance {-# OVERLAPPABLE #-} Bad a b where 
    bad = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Bad (Id a) (C a) where 
    bad = \_ -> "Id" 

class Good a b where 
    good :: a -> String 

instance {-# OVERLAPPABLE #-} Good a b where 
    good = \_ -> "Default case" 

instance {-# OVERLAPPING #-} Good (Id a) b where 
    good = \_ -> "Id" 

test = let a = Id() "a" 
     in putStrLn (good a) >> putStrLn (bad a) 

(需要注意的是,除非你對此有何評論第二個壞情況下這不會編譯)

類好的作品沒有任何問題(測試輸出「Id」)。如果我不刪除第二個實例壞了,我得到:

Overlapping instances for Bad (Id()) b0 
    arising from a use of ‘bad’ 
    Matching instances: 
    instance [overlappable] Bad a b -- Defined at Instance.hs:12:31 
    instance [overlapping] Bad (Id a) (C a) 
     -- Defined at Instance.hs:15:30 
    (The choice depends on the instantiation of ‘b0’ 
    To pick the first instance above, use IncoherentInstances 
    when compiling the other instance declarations) 
    In the first argument of ‘putStrLn’, namely ‘(bad a)’ 
    In the second argument of ‘(>>)’, namely ‘putStrLn (bad a)’ 
    In the expression: putStrLn (good a) >> putStrLn (bad a) 

我不明白的是爲什麼會發生這種情況,當他們之間的唯一區別是在第二類參數的aditional的限制。

另外,是不是可重疊實例的重點,以避免重疊錯誤?

問候

+0

這些類不會真正起作用 - 類中的函數沒有提到變量'b',並且沒有fundep或類似的從'a'確定'b'。 「好」只能在非常巧合的情況下才起作用 - 在「好」這兩個例子中,第二種類型是完全不受限制的類型變量。這基本上與你沒有類中的類型變量相同。 'bad'不起作用,因爲'b'是一個完全自由的類型變量,它可能會在稍後的時間被實例化爲'C a',這將改變選擇哪個實例(「選擇取決於實例化」)。 – user2407038

+0

我認爲你的編譯指示應該有'AllowAmbiguousTypes'而不是'UndecidableInstances',否則你會得到一個不同的錯誤,這個錯誤與函數簽名'bad :: Bad ab => a - > String'中的'b'有關係... – Alec

+0

@ user2407038是的,我可以看到,儘管如果將b添加到函數中,錯誤仍然存​​在。再測試一下,我看到你基本上必須使用這個函數,而明確地說你的b是什麼,這樣ghc知道使用哪個實例。我可以得到這個小例子的工作,但我認爲這在更大的項目中是不可行的。謝謝! –

回答

3

按照我上面的評論,我覺得你的編譯指示應該有AllowAmbiguousTypes而不是UndecidableInstances, 否則你會得到一個不同的錯誤(至少我是這樣的GHC 8.0.1)有關b是曖昧功能簽名 bad :: Bad a b => a -> String

AmbiguousTypes允許您爲使用時不明確的函數編寫簽名。 取而代之,歧義檢查將移至呼叫地點。這與TypeApplications 之類的東西非常相似,可以指定那些不明確的變量。在這種情況下,bad始終不明確,因此我們需要此編譯指示移至呼叫站點的 錯誤消息。現在,我有和你一樣的信息。


,即使OVERLAPPABLEOVERLAPPING哈斯克爾抱怨的原因是,根據b如何實例化(尚未 指定),它會選擇Bad兩個實例之一。換句話說,你可以bC a統一, 一樣好,你不能,所以Haskell中拋出了它的手,說:「你還沒有告訴我有足夠的瞭解b我要 能夠挑選Bad的最具體實例「。

在另一方面,即使不知道b,哈斯克爾知道哪些實例Good a bGood (Id a) b都比較 具體的 - 它始終是第二個(甚至不知道什麼b是這樣的話)。

我真的推薦你閱讀關於overlapping instances 的文檔,因爲它解釋了整個算法。


您通常可以使用周圍事物一樣TypeApplications(指定b),或翻譯您類型類,一類家庭這些問題得到。