我知道这是代码有点傻,但有人可以解释为什么这个isList [42]
返回True
而isList2 [42]
打印False
,以及如何防止这种情况?我想更好地理解一些比较模糊的GHC类型扩展,我认为这将是一个有趣的例子。
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE IncoherentInstances #-}
class IsList a where
isList :: a -> Bool
instance IsList a where
isList x = False
instance IsList [a] where
isList x = True
isList2 = isList
main =
print (isList 42) >>
print (isList2 42) >>
print (isList [42]) >>
print (isList2 [42])
答案 0 :(得分:15)
这真的很简单。让我们问GHCi isList2
的类型是什么:
∀x. x ⊢ :t isList2
isList2 :: a -> Bool
这与[a]
实例不匹配(即使它可以通过统一),但它确实与a
实例立即匹配。因此,GHC会选择a
个实例,因此isList2
会返回False
。
此行为正是IncoherentInstances
的含义。实际上,这是一个相当不错的演示。
非常有趣的是,如果你只是禁用IncoherentInstances
,我们会得到完全相反的效果,GHCi现在这样说:
∀x. x ⊢ :t isList2
isList2 :: [Integer] -> Bool
这是因为isList2
是未使用函数语法定义的顶级绑定,因此受到Dreaded Monomorphism Restriction的限制。因此它专门用于实际使用的实例。
添加NoMonomorphismRestriction
以及停用IncoherentInstances
,我们会改为:
∀x. x ⊢ :t isList2
isList2 :: IsList a => a -> Bool
∀x. x ⊢ isList2 'a'
False
∀x. x ⊢ isList2 "a"
True
∀x. x ⊢ isList2 undefined
<interactive>:19:1:
Overlapping instances for IsList a0 arising from a use of `isList2'
这是预期的重叠行为,如果选择不明确,则根据使用和投诉选择实例。
关于问题的编辑,我不相信没有类型注释就可以获得所需的结果。
第一个选项是为isList2
提供类型签名,以防止IncoherentInstances
过早选择实例。
isList2 :: (IsList a) => a -> Bool
isList2 = isList
你可能需要在其他任何地方(即使是间接地)提及isList
而不应用于参数。
第二个选项是消除数字文字的歧义并禁用IncoherentInstances
。
main =
print (isList (42 :: Integer)) >>
print (isList2 (42 :: Integer)) >>
print (isList [42]) >>
print (isList2 [42])
在这种情况下,有足够的信息来选择最具体的实例,因此OverlappingInstances
可以做到这一点。
答案 1 :(得分:2)
以下code无需IncoherentInstances
即可完成此操作:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverlappingInstances #-}
class IsList a where
isList :: a -> Bool
instance IsList a where
isList x = False
instance IsList [a] where
isList x = True
isList2 :: (IsList a) => a -> Bool
isList2 = isList
main = do
print (isList (42 :: Int))
print (isList [42 :: Int])
print (isList2 (42 :: Int))
print (isList2 [42 :: Int])
我建议不要使用IncoherentInstances
,这似乎会造成很多麻烦,因为你可以很容易地默默地调用不同的重载。