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?

0


source to share


1 answer


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

      

+1


source







All Articles