Haskell 似乎不重叠的重叠实例
考虑以下接近最小的示例:Haskell 似乎不重叠的重叠实例,haskell,Haskell,考虑以下接近最小的示例: {-# 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
{-# 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结果,因为我们有实例谓词Bar和谓词a=>Predicate Baz a
但GHC 8.6.3和8.0.1都拒绝这一点:
测试。hs:18:16:错误:
•谓词Baz Bar的重叠实例
因使用“测试”而产生
匹配实例:
实例谓词a,可遍历的t=>谓词TA
-在试验中定义。hs:6:10
实例谓词a=>谓词Baz a
-在试验中定义。hs:14:10
•在“$”的第二个参数中,即“test$Baz Bar”
在表达式中:print$test$Baz Bar
在“main”的公式中:main=print$test$Baz条
|
18 | main=打印$test$Baz条
| ^^^^^^^^^^^^^^
但是没有重叠:我们可以通过注释掉谓词Baz a instance来确认没有可遍历Baz的实例,在这种情况下,我们会得到错误:
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决定独立于实例上的约束使用哪个实例的结果,如前所述。不过,这种伎俩在这里似乎不起作用:
instance (b ~ Baz, Predicate a) => Predicate (b a) where
给出了一个重复的实例声明错误,因此我将问题留给一个在这种情况下有效的解决方案。问题是这些实例确实重叠,因为实例解析机制在决定采用哪个实例时只会查看实例头,只有在选择了一个实例之后,它检查约束是否满足,否则抛出错误 我建议您阅读有关的文档 除了重新设计解决方案(这可能是正确的做法)之外,解决问题的一种方法是告诉GHC某个实例不太重要或可重叠。 这基本上意味着GHC将选择一个更具体的实例,如果它可用,您可以在上面链接的文档中阅读更具体的方法。 这是通过使用pragma{-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
正如预期的那样。问题在于,这些实例确实重叠,因为实例解析机制在决定采用哪个实例时只会查看实例头,并且只有在选择实例之后,才会检查约束以查看是否满足,否则会抛出错误 我建议您阅读有关的文档 除了重新设计解决方案(这可能是正确的做法)之外,解决问题的一种方法是告诉GHC某个实例不太重要或可重叠。 这基本上意味着GHC将选择一个更具体的实例,如果它可用,您可以在上面链接的文档中阅读更具体的方法。 这是通过使用pragma{-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
正如所料