我收到错误:
Duplicate instance declarations:
instance [overlap ok] EnumTag a => Read a
-- Defined at /XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/intero/intero2932Xpa-TEMP.hs:110:27
instance [overlap ok] StrTag a => Read a
-- Defined at /XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/intero/intero2932Xpa-TEMP.hs:121:27 (intero)
对于此代码:
class (Show a, Enum a) => EnumTag a where
anyEnum :: a
instance {-# OVERLAPS #-} EnumTag a => Read a where
readPrec = RP.lift P.skipSpaces >> expectEnum
instance {-# OVERLAPS #-} EnumTag a => Eq a where
a == b | a == anyEnum || b == anyEnum = True
| otherwise = fromEnum a == fromEnum b
class StrTag a where
anyStr :: a
tagPrefix :: a -> String -- ^ should be constant
toStr :: String -> a
instance {-# OVERLAPS #-} StrTag a => Read a where
readPrec = parens $ do
RP.lift P.skipSpaces
(RP.lift $ expectShown anyStr) <++ RP.lift g
where g = do
Just s@(_:_) <- L.stripPrefix tagPrefix <$> expectTag
return $ toStr s
为什么会这样?第一个实例中的Read a
仅在a
为EnumTag
时有效,在第二个实例中 - 仅在a
为StrTag
时才有效。
如何修复此错误并创建&#34;默认&#34; EnumTag
和StrTag
的实例,因此客户端代码将继承&#34;继承&#34;那些功能(阅读)很简单,只有实例化EnumTag
或StrTag
?
答案 0 :(得分:3)
重叠实例非常脆弱。可以说,永远不应该使用它们。
无论如何,重叠的实例会导致GHC过早地避免提交实例。 E.g。
-- overlapping
instance ... => Read [Int] -- 1
instance ... => Read [a] -- 2
当GHC必须解决诸如Read [b]
之类的约束时,它不会提交到实例2,因为它不能排除更具体的实例1:在所有类型变量{{1}之后以后可能会发现b
。
如果稍后发现Int
为b
,则会选择实例1。如果发现Int
为b
,则GHC将选择实例2。
然而,这要求有一个最好的选择&#34;在每种情况下。即当String
时,两个实例都可以使用,但是1更具体,因此它是最好的#34; GHC选择它。
如果您有实例
b = Int
然后没有最好的实例存在,因为它们使用相同的头instance ... => Read [a] -- 3
instance ... => Read [a] -- 4
,使得选择本质上不明确,GHC拒绝它。
可能让您感到困惑的是,GHC将从不考虑Read [a]
左侧的内容(&#34; context&#34;)来选择一个实例。一旦你考虑它们的上下文,实例是否会重叠并不重要,因为没有类型可以同时满足3和4的上下文。检测上下文是互斥的是一般的非常困难,而且GHC甚至都没有尝试这样做。仅考虑实例头(=>
右侧)。
除非客户端定义更具体的实例,否则无法创建自动打开的两个默认实例。
我更喜欢在实例中避免重叠,并在实例外部提供默认实现。 E.g。
=>
如果客户想要使用它,他们可以使用
启用默认实现-- library
readPrec_fromStrTag :: StrTag a => Int -> ReadS a
readPrec_fromStrTag = parens $ do
RP.lift P.skipSpaces
(RP.lift $ expectShown anyStr) <++ RP.lift g
where g = do
Just s@(_:_) <- L.stripPrefix tagPrefix <$> expectTag
return $ toStr s
如果这个和其他样板文件变得太麻烦,模板Haskell可能会帮助用户写出来。