Show 的重叠实例
Overlapping instance for Show
假设我们有以下内容:
{-# 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
,但是它的签名有点外国。我已经有了一个函数,我打算用它来显示某些列表,直接 returns String
。
在您的示例中,您只需删除 instance (Show a, ListContainer l) => Show (l a)
并将 deriving (Show)
添加到 L
定义。
或者您可以从 A
定义中删除 deriving (Show)
。
如果您希望代码按现在的方式运行,请移除 deriving (Show)
并显式实现它
instance {-# OVERLAPPING #-} Show a => Show (A a)
where
show (A a) = "A " ++ show a
I can understand that it thinks type a
can either derive Show
, or derive ListContainer
, which may result in Show
.
这不是它的想法。
当Haskell选择class实例时,它根本不考虑实例约束。选择实例时,它只考虑实例头(紧跟在 class 名称之后的东西)。
在您的 Show
实例中,实例头是 l a
。此实例头匹配 A a
(假设 l = A
)。顺便说一下,它还匹配很多其他东西 - 例如,它匹配 Maybe a
(其中 l = Maybe
)和 Either b a
(l = Either b
)和 Identity a
, 和 IO a
- 几乎每个类型都有一个类型参数,想一想。 A
、Maybe
和 IO
都没有 ListContainer
的实例并不重要,因为就像我上面说的,Haskell 不看选择实例时的约束,仅在实例头。
只有在 找到一个匹配的实例(通过匹配它的头部)后,Haskell 才会检查该实例的约束是否确实得到满足。如果不是,他们会抱怨。但它永远不会返回并尝试选择另一个实例。
所以回到你的例子:因为 A
现在有两个匹配的 Show
实例 - 它自己的派生实例和你编写的 Show (l a)
实例, - 编译器抱怨说它们重叠。
假设我们有以下内容:
{-# 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
,但是它的签名有点外国。我已经有了一个函数,我打算用它来显示某些列表,直接 returns String
。
在您的示例中,您只需删除 instance (Show a, ListContainer l) => Show (l a)
并将 deriving (Show)
添加到 L
定义。
或者您可以从 A
定义中删除 deriving (Show)
。
如果您希望代码按现在的方式运行,请移除 deriving (Show)
并显式实现它
instance {-# OVERLAPPING #-} Show a => Show (A a)
where
show (A a) = "A " ++ show a
I can understand that it thinks type
a
can either deriveShow
, or deriveListContainer
, which may result inShow
.
这不是它的想法。
当Haskell选择class实例时,它根本不考虑实例约束。选择实例时,它只考虑实例头(紧跟在 class 名称之后的东西)。
在您的 Show
实例中,实例头是 l a
。此实例头匹配 A a
(假设 l = A
)。顺便说一下,它还匹配很多其他东西 - 例如,它匹配 Maybe a
(其中 l = Maybe
)和 Either b a
(l = Either b
)和 Identity a
, 和 IO a
- 几乎每个类型都有一个类型参数,想一想。 A
、Maybe
和 IO
都没有 ListContainer
的实例并不重要,因为就像我上面说的,Haskell 不看选择实例时的约束,仅在实例头。
只有在 找到一个匹配的实例(通过匹配它的头部)后,Haskell 才会检查该实例的约束是否确实得到满足。如果不是,他们会抱怨。但它永远不会返回并尝试选择另一个实例。
所以回到你的例子:因为 A
现在有两个匹配的 Show
实例 - 它自己的派生实例和你编写的 Show (l a)
实例, - 编译器抱怨说它们重叠。