1

假设我们有以下内容:

{-# LANGUAGE FlexibleInstances #-}

module Sample where

newtype A a =
  A a
  deriving (Show)

newtype L a =
  L [a]

class ListContainer l where
  getList :: l a -> [a]

instance ListContainer L where
  getList (L l) = l

instance (Show a, ListContainer l) => Show (l a) where
  show = const "example"

使用此代码,ghc 抱怨:

warning: [-Wdeferred-type-errors]
• Overlapping instances for Show (A a)
    arising from a use of ‘GHC.Show.$dmshowList’
  Matching instances:
    instance (Show a, ListContainer l) => Show (l a)
      -- Defined at /.../src/Sample.hs:18:10
    instance Show a => Show (A a)
      -- Defined at /.../src/Sample.hs:7:13
• In the expression: GHC.Show.$dmshowList @(A a)
  In an equation for ‘showList’:
      showList = GHC.Show.$dmshowList @(A a)
  When typechecking the code for ‘showList’
    in a derived instance for ‘Show (A a)’:
    To see the code I am typechecking, use -ddump-deriv
  In the instance declaration for ‘Show (A a)’
warning: [-Wdeferred-type-errors]
• Overlapping instances for Show (A a)
    arising from a use of ‘GHC.Show.$dmshow’
  Matching instances:
    instance (Show a, ListContainer l) => Show (l a)
      -- Defined at /.../src/Sample.hs:18:10
    instance Show a => Show (A a)
      -- Defined at /.../src/Sample.hs:7:13
• In the expression: GHC.Show.$dmshow @(A a)
  In an equation for ‘show’: show = GHC.Show.$dmshow @(A a)
  When typechecking the code for ‘show’
    in a derived instance for ‘Show (A a)’:
    To see the code I am typechecking, use -ddump-deriv
  In the instance declaration for ‘Show (A a)’

我可以理解它认为类型a可以派生Show或派生ListContainer,这可能会导致Show.

我们如何避免这种情况?

我知道存在一个 function showList,但它的签名有点陌生。我已经有一个函数,我打算用来显示某些列表,它String直接返回。

4

2 回答 2

5

我可以理解它认为类型 a 可以派生 Show或派生 ListContainer,这可能会导致 Show.

它不是这么想的。

当 Haskell 选择类实例时,它根本不考虑实例约束。它在选择实例时只考虑实例头(紧跟在类名之后的东西)。

在您的Show实例中,实例头是l a. 此实例头部匹配A a(通过假设l = A)。顺便说一句,它还匹配很多其他的东西——例如,它匹配Maybe a(where l = Maybe)、and Either b a(with l = Either b)、andIdentity aIO a- 几乎所有带有类型参数的类型,想想看。A既没有MaybeIO没有 的实例并不重要ListContainer,因为就像我上面所说的,Haskell 在选择实例时不考虑约束,只考虑实例头部。

只有找到一个匹配的实例(通过匹配它的头部)之后,Haskell 才会检查该实例的约束是否真的得到满足。如果他们不是,他们会抱怨。但它永远不会回去尝试选择另一个实例。

所以回到你的例子:因为A现在有两个匹配Show的实例——它自己派生的一个和Show (l a)你写的那个——编译器抱怨它们是重叠的。

于 2019-05-02T12:16:44.537 回答
2

在您的示例中,您可以删除instance (Show a, ListContainer l) => Show (l a)并添加deriving (Show)L定义中。

或者,您可以deriving (Show)A定义中删除。

如果您希望您的代码表现得像现在一样删除deriving (Show)并显式实现它

 instance {-# OVERLAPPING #-}  Show a => Show (A a)
      where 
         show (A a) = "A " ++ show a
于 2019-05-02T08:18:32.900 回答