2017-07-25 57 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”时注册的内容,恐怕。 –