首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有"overlaps“的默认实例实现

带有"overlaps“的默认实例实现
EN

Stack Overflow用户
提问于 2017-12-01 19:48:07
回答 1查看 203关注 0票数 0

我得到了错误:

代码语言:javascript
复制
 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)

对于此代码:

代码语言:javascript
复制
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只有当aEnumTag时才有效,第二个实例中-只有当aStrTag时才有效。

如何修复此错误并为EnumTagStrTag创建“默认”实例,以便客户端代码将简单地“继承”这些功能(读取),只需实例化EnumTagStrTag

EN

回答 1

Stack Overflow用户

发布于 2017-12-01 20:31:54

重叠的实例非常脆弱。可以说,它们永远不应该被使用。

无论如何,重叠的实例会导致GHC避免过早提交实例。例如。

代码语言:javascript
复制
-- overlapping
instance ... => Read [Int]  -- 1
instance ... => Read [a]    -- 2

当GHC必须解决诸如Read [b]之类的约束时,它不会提交到实例2,因为它不能排除更具体的实例1:毕竟,类型变量b稍后可能会被发现为Int

如果稍后发现bInt,则将选择实例1。如果发现bString,则GHC将选择instance 2。

然而,这要求在每种情况下都有一个“最佳选择”。也就是说,当b = Int时,两个实例都可以使用,但1更具体,因此它是“最好的”,而GHC选择它。

代码语言:javascript
复制
instance ... => Read [a] -- 3
instance ... => Read [a] -- 4

那么就不存在最好的实例,因为它们使用相同的头Read [a],这使得选择本质上是模棱两可的,而GHC拒绝了这一点。

可能让你感到困惑的是,GHC永远不会考虑=> (“上下文”)左边的内容来选择一个实例。一旦考虑到实例的上下文,实例是否会重叠并不重要,因为没有任何类型可以同时满足3和4的上下文。一般来说,检测上下文互斥是非常困难的,GHC甚至不会尝试这样做。仅考虑实例头部(在=>的右侧)。

除非客户端定义了一个更具体的实例,否则无法创建这两个自动打开的默认实例。

我更喜欢避免实例中的重叠,并在实例之外提供默认实现。例如。

代码语言:javascript
复制
-- 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

如果客户端想要使用它,他们可以使用以下命令启用默认实现

代码语言:javascript
复制
instance StrTag T where
   ...
instance Read T where
   readPrec = readPrec_fromStrTag  -- use default from the library

如果这个模板和其他模板变得太麻烦,模板Haskell可能会帮助用户编写它。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47592742

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档