假设我们有以下内容:
{-# 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
。
答案 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 a
,A
和Maybe
都没有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