Show

时间:2019-05-02 07:41:13

标签: haskell overlapping-instances

假设我们有以下内容:

{-# 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

我们如何避免这种情况?

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

2 个答案:

答案 0 :(得分:4)

  

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

那不是它的想法。

当Haskell选择类实例时,它根本不会查看实例约束。选择一个实例时,它所考虑的只是实例头(在类名后面紧随其后的东西)。

在您的Show实例中,实例头为l a。该实例头与A a匹配(假设l = A)。顺便说一句,它还与许多其他事物匹配-例如,它与Maybe a(其中l = Maybe),Either b a(与l = Either b)和{{1 }}和Identity a-几乎每个带有类型参数的类型都会想到它。 IO aAMaybe都没有IO的实例并不重要,因为就像我上面说的那样,Haskell在选择实例时不会查看约束,仅在实例头部。

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

所以回到您的示例:由于ListContainer现在有两个匹配的A实例-它自己的派生实例和您编写的Show实例,-编译器抱怨它们是重叠。

答案 1 :(得分:1)

在您的示例中,您只需删除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