When defining a class (* & # 8594; *) comes up a generic solution for instances (Eq, Show) of overlapping instances,
There are many threads on the stack on overlapping instances, and while they are useful for explaining the source of the problem, I still don't understand how to reverse engineer my code to fix the problem. While I will confidently put more time and effort into reviewing the details of existing answers, I will post here a general outline that I have identified as causing the problem, in the hope that there is a simple and general answer: I usually find myself defining a class such as:
{-# LANGUAGE FlexibleInstances #-}
class M m where
foo :: m v -> Int
bar :: m v -> String
along with instance declarations:
instance (M m) => Eq (m v) where
(==) x y = (foo x) == (foo y) -- details unimportant
instance (M m) => Show (m v) where
show = bar -- details unimportant
and in the course of my work I will inevitably create some data type:
data A v = A v
and declare A
as an instance of the class M
:
instance M A where
foo x = 1 -- details unimportant
bar x = "bar"
Then we define some elements from A Integer
:
x = A 2
y = A 3
I have no problem printing x
and y
or evaluating Boolean x == y
, but if I try to print a list [x]
or evaluate Boolean [x] == [y]
, then an overlapping instance error occurs:
main = do
print x -- fine
print y -- fine
print (x == y) -- fine
print [x] -- overlapping instance error
if [x] == [y] then return () else return () -- overlapping instance error
The reason for these errors is now very clear. I think: they stem from existing instance declarations instance Show a => Show [a]
and instance Eq a => Eq [a]
, and while it's true that it [] :: * -> *
hasn't been declared as an instance of my class M
yet, nothing prevents someone from doing this at some point: so the compiler ignores the context of the instance declarations.
When faced with the pattern I described, how can you reverse engineer it to avoid the problem?
source to share
If you search for an instance, there is no return. Instances are matched solely based on the syntax structure of the instance chapter. This means that instance contexts are not considered when resolving the instance.
So when you write
instance (M m) => Show (m v) where
show = bar
you say, "Here's an example Show
for any type of form m v
." Since [x] :: [] (A Int)
it is indeed a type of form m v
(set m ~ []
and v ~ A Int
), looking up an instance for Show [A Int]
invokes two candidates:
instance Show a => Show [a]
instance M m => Show (m v)
As I said, the type checker does not consider instance contexts when selecting an instance, so the two instances overlap.
The fix is ββto not declare instances of the type Show (m v)
. It is generally a bad idea to declare instances whose head consists solely of type variables. Every instance you write must start with a fair-to-perfect constructor, and you should be suspicious of instances that do not match this pattern.
The newtype
default delivery for your instances is pretty standard design (see instance for example ),WrappedBifunctor
Functor
newtype WrappedM m a = WrappedM { unwrapM :: m a }
instance M m => Show (WrappedM m a) where
show = bar . unwrapM
since it provides a standard implementation of the function at the top level (see for example foldMapDefault
):
showDefault = bar
source to share