2017-07-25 37 views
0

我一直在嘗試用這種簡單的實現HLists和功能hasInt返回True如果Int是名單中的一員:如果缺少約束,GHC使用catch-all實例?

{-# LANGUAGE FlexibleInstances #-} 

data HNil = HNil 
    deriving (Show, Read) 

data HCons a b = HCons a b 
    deriving (Show, Read) 

class HasInt a where 
    hasInt :: a -> Bool 

instance HasInt HNil where 
    hasInt _ = False 

instance HasInt as => HasInt (HCons a as) where 
    hasInt (HCons a as) = (isInt a) || (hasInt as) 

class IsInt a where 
    isInt :: a -> Bool 

instance IsInt Int where 
    isInt _ = True 

instance {-# OVERLAPPABLE #-} IsInt a where 
    isInt _ = False 

three = 3 :: Int 

main = do 
    putStrLn $ "isInt three = " ++ show (isInt three) -- True 
    putStrLn $ "isInt True = " ++ show (isInt True) -- False 
    print $ hasInt $ HCons three $ HCons True HNil -- False ??? 

這不會產生預期的結果。然而,它似乎工作,如果我改變:

instance HasInt as => HasInt (HCons a as) where 

到:

instance (IsInt a, HasInt as) => HasInt (HCons a as) where 

在另一方面,我通常希望GHC抱怨,如果我用一個類型類的功能,但不包括約束條件,在這種情況下我沒有得到任何指示。

很明顯,它必須做一些事情,全面實例IsInt a。我將得到Could not deduce (IsInt a) arising from a use of 'isInt' 錯誤,如果我取代包羅萬象的實例:

instance IsInt Bool where isInt _ = False 
instance IsInt HNil where isInt _ = False 

我的問題是:是GHC的這個預期的行爲 - 如果沒有明確它會悄悄地用一個包羅萬象的實例類型類約束?

+0

我沒有在這裏看到任何'instance IsInt Int where isInt _ = True',這讓我很困惑你如何得到任何返回True的東西。 – amalloy

+0

是的 - 我錯過了那條線。問題已更新。 – ErikR

+0

這是重疊實例太可怕的原因之一。如果你遺漏了一個實例,所有的東西都會被編譯,一切都會錯誤的。 – dfeuer

回答

3

是的,這是預期的行爲。如果你寫

instance Foo a 

您聲明,所有類型的Foo實例,GHC 認爲你。

這是100%,類似以下內容:

foo :: Int -> Bool 
foo x = x > 0 

即使你沒有在上下文Ord Int,GHC知道有這樣一個實例。同樣,在:

bar :: a -> b 
bar x = {- use the Foo instance for x here -} 

即使你沒有Foo a的背景下,GHC知道有這樣一個實例。

+0

我想我明白了,但令人驚訝的是,代碼的行爲有所不同,取決於約束是否存在,並且我沒有從GHC得到任何指示,無論是以錯誤還是警告的形式,我可能會爲了一個驚喜。 – ErikR

+1

@ErikR這就是您在啓用「OverlappingInstances」時註冊的內容,恐怕。 –