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
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 决定使用哪个实例而不受实例约束的结果,如此处所述。不过,这个技巧在这里似乎不起作用:
instance (b ~ Baz, Predicate a) => Predicate (b a) where
给出一个Duplicate instance declarations
错误,所以我将这个问题留给在这种情况下有效的解决方案。
解决方案
问题是这些实例确实重叠,因为实例解析机制在决定采用哪个实例时只查看实例头,并且仅在稍后选择实例后,它才会检查约束以查看是否满足(并且否则抛出和错误)。
我建议阅读有关实例解析的文档
解决问题的一种方法(除了重新设计解决方案,这可能是正确的做法)是告诉 GHC 某个实例“不太重要”(或可重叠)。
这基本上意味着 GHC 将选择一个更具体的实例(如果它可用)(更具体的意思是您可以在上面链接的文档中阅读)。
这是通过使用 pragma {-# OVERLAPPABLE #-}
or来实现的{-# 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
正如预期的那样。
推荐阅读
- flutter - 在 Vscode 中将 Dart SDK 从 2.10.3 更新到 2.12.0
- spring-webflux - 一个标准完整的 REST 操作如何使用 Spring WebClient
- jquery - Jquery 自动求和
- python - 使用 period_range 在 Pandas DataFrame 中生成新行
- javascript - 如何在ejs中设置颜色
- python - 我已经将 pandas 的对象列转换为日期时间,但是在我用 to_csv 保存并重新加载它之后,列数据类型变回对象
- java - 将对象与java中的前一个实例进行比较
- python - python中是否有一个函数可以让我识别这个紫色圆圈的位置?
- reactjs - Safari 问题与单页应用程序反应与 AWS (Cloudfront/S3)
- sql - 如何在不硬编码年份的情况下比较 SQL 数据库中的年份变化?