考虑以下(几乎是最少的)示例:
{-# Language FlexibleInstances #-}
class Predicate a where
test :: a -> Bool
instance (Predicate a, Traversable t) => Predicate (t a) where
test = all test
data Bar = Bar
instance Predicate Bar where
test Bar = False
data Baz a = Baz a
instance Predicate a => Predicate (Baz a) where
test (Baz x) = test x
main :: IO ()
main = print $ test $ Baz Bar
看看test $ Baz Bar
,您会期望得到False
的结果,因为我们有实例Predicate Bar
和Predicate a => Predicate (Baz a)
。
但是GHC 8.6.3和8.0.1都拒绝这样做:
test.hs:18:16: error:
• Overlapping instances for Predicate (Baz Bar)
arising from a use of ‘test’
Matching instances:
instance (Predicate a, Traversable t) => Predicate (t a)
-- Defined at test.hs:6:10
instance Predicate a => Predicate (Baz a)
-- Defined at test.hs:14:10
• In the second argument of ‘($)’, namely ‘test $ Baz Bar’
In the expression: print $ test $ Baz Bar
In an equation for ‘main’: main = print $ test $ Baz Bar
|
18 | main = print $ test $ Baz Bar
| ^^^^^^^^^^^^^^
而且没有重叠:我们可以通过注释掉Traversable Baz
实例来确认没有Predicate (Baz a)
实例,在这种情况下,我们会得到错误:
test.hs:18:16: error:
• No instance for (Traversable Baz) arising from a use of ‘test’
• In the second argument of ‘($)’, namely ‘test $ Baz Bar’
In the expression: print $ test $ Baz Bar
In an equation for ‘main’: main = print $ test $ Baz Bar
|
18 | main = print $ test $ Baz Bar
| ^^^^^^^^^^^^^^
我假设这是FlexibleInstances
的限制?如果是这样,为什么,并且有批准的解决方法?
好的,事实证明,这是GHC决定对哪个实例使用独立于实例约束的 的结果,如here所述。该技巧似乎在这里似乎不起作用:
instance (b ~ Baz, Predicate a) => Predicate (b a) where
给出一个Duplicate instance declarations
错误,所以我将问题悬而未决,以寻求在这种情况下有效的解决方案。
答案 0 :(得分:12)
问题是这些实例确实重叠,因为实例解析机制仅在决定采用哪个实例时才查看实例头,并且仅在选择实例后的 之后才进行检查。约束,以确保它得到满足(否则引发并出错)。
我建议阅读instance resolution上的文档
解决问题的一种方法(除了重新设计解决方案之外,这可能是正确的选择),是告诉GHC某个实例“不那么重要”(或可重叠)。
这基本上意味着GHC将选择一个更具体的实例(如果有的话)(更具体的意思是您可以阅读上面链接的文档)。
这可以通过使用杂注{-# OVERLAPPABLE #-}
或{-# OVERLAPS #-}
来实现(请阅读文档以了解不同之处,基本上前者更为具体)。
结果代码如下所示
{-# Language FlexibleInstances #-}
class Predicate a where
test :: a -> Bool
instance {-# OVERLAPPABLE #-} (Predicate a, Traversable t) => Predicate (t a) where
test = all test
data Bar = Bar
instance Predicate Bar where
test Bar = False
data Baz a = Baz a
instance Predicate a => Predicate (Baz a) where
test (Baz x) = test x
main :: IO ()
main = do
print . test $ Baz Bar
print . test $ ([] :: [Bar])
print . test $ [Bar]
print . test $ Baz ([] :: [Bar])
运行结果为
False
True
False
True
符合预期。