我目前正在从事一个项目,在该项目中我派生了类的一些实例。由于该类只有一种方法,除了少数特定情况外,具有相同的定义,因此我尝试定义一个可重叠的通用实例,然后定义需要重叠的实例。
这不起作用,因为我收到重叠实例错误。通过进行一些测试,我们遇到了这个简化的示例,它几乎相当于我原来的问题:
{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses #-}
module Instance where
data Id a = Id a String
data C a = C a
class Bad a b where
bad :: a -> String
instance {-# OVERLAPPABLE #-} Bad a b where
bad = \_ -> "Default case"
instance {-# OVERLAPPING #-} Bad (Id a) (C a) where
bad = \_ -> "Id"
class Good a b where
good :: a -> String
instance {-# OVERLAPPABLE #-} Good a b where
good = \_ -> "Default case"
instance {-# OVERLAPPING #-} Good (Id a) b where
good = \_ -> "Id"
test = let a = Id () "a"
in putStrLn (good a) >> putStrLn (bad a)
(请注意,除非您注释第二个 Bad 实例,否则不会编译。)
Class Good 工作没有任何问题(测试输出“Id”)。如果我不删除 Bad 的第二个实例,我会得到:
Overlapping instances for Bad (Id ()) b0
arising from a use of ‘bad’
Matching instances:
instance [overlappable] Bad a b -- Defined at Instance.hs:12:31
instance [overlapping] Bad (Id a) (C a)
-- Defined at Instance.hs:15:30
(The choice depends on the instantiation of ‘b0’
To pick the first instance above, use IncoherentInstances
when compiling the other instance declarations)
In the first argument of ‘putStrLn’, namely ‘(bad a)’
In the second argument of ‘(>>)’, namely ‘putStrLn (bad a)’
In the expression: putStrLn (good a) >> putStrLn (bad a)
我不明白的是为什么会发生这种情况,因为它们之间的唯一区别是第二类参数的附加限制。
此外,可重叠实例的目的不是为了避免重叠错误吗?
问候
最佳答案
根据我上面的评论,我认为你的编译指示应该有 AllowAmbigouslyTypes
而不是 UndecidableInstances
,
否则你会得到一个不同的错误(至少我在 GHC 8.0.1 上是这样),与函数签名中的 b
不明确有关
bad::Bad a b => a -> String
。
AmbigouslyTypes
允许您为使用时不明确的函数编写签名。
相反,歧义性检查被移至调用站点。这对于像 TypeApplications 这样的东西非常有效
来指定那些不明确的变量。在这种情况下,bad
总是不明确的,所以我们需要这个 pragma 移动到
调用站点的错误消息。现在,我有和你一样的信息。
即使使用 OVERLAPPABLE
和 OVERLAPPING
Haskell 提示的原因是,取决于 b
的实例化方式(尚未被实例化)
指定),它将选择 Bad
的两个实例之一。换句话说,您可能希望b
与C a
统一,
就像你不能做的那样,所以 Haskell 举起双手并说“你还没有告诉我足够多的关于 b
的信息,让我
能够选择 Bad
的最具体实例。
另一方面,即使不知道 b
,Haskell 也知道 Good a b
和 Good (Id a) b
实例中的哪一个是更多的
具体 - 它始终是第二个(即使不知道 b
是什么)。
我真的建议您阅读有关 overlapping instances 的文档 因为它解释了整个算法。
您通常可以使用诸如 TypeApplications
(指定 b
)之类的方法来解决这些问题,或者将您的类型类转换为类型系列。
关于haskell - 重叠实例的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39230522/