mb14
mb14

Reputation: 22596

How to resolve overlapping instance

I have the following code (transform is similar to convert)

instance {-# OVERLAPS #-} Transformable a a where
  transform x = x

instance {-# OVERLAPPABLE #-} (Transformable l l',   Transformable r r' )
         => Transformable (Either l r) (Either l' r')
  where
    transform = bimap transform transform

Of course, those instances overlap in the case where I'm trying to transform Either a b to Either a b and get the following error message (ParsingError is a type alias for Either something somethingElse)

    Overlapping instances for Transformable
                                (parsingerror text) (parsingerror text)
      arising from a use of ‘makereceipt’
    matching instances:
Matching instances:    Overlapping instances for Transformable
                            (ParsingError Text) (ParsingError Text)
  arising from a use of ‘makeReceipt’
Matching instances:
  instance [overlappable] (Transformable l l', Transformable r r') =>
                          Transformable (Either l r) (Either l' r')
      instance [overlappable] (Transformable l l', Transformable r r') =>
                              Transformable (Either l r) (Either l' r')
        -- Defined at Handler/GLEnterReceiptSheet/ReceiptRow.hs:154:31
      instance [overlap ok] Transformable a a
        -- Defined at Handler/GLEnterReceiptSheet/ReceiptRow.hs:151:27

I tried different combination of OVERLAPS, OVERLAPPING and OVERLAPPABLE but nothing works. How can I solve this ?

Upvotes: 11

Views: 6978

Answers (1)

user2407038
user2407038

Reputation: 14578

You will have to change one of the instance definitions:

class Transformable a b where 
  transform :: a -> b 

-- this one
instance {-# OVERLAPS #-} (a ~ b) => Transformable a b where
  transform x = x

instance (Transformable l l', Transformable r r' )
       => Transformable (Either l r) (Either l' r') where
  transform = either (Left . transform) (Right . transform) 

test0 :: (Transformable a a', Transformable b b') => Either a b -> Either a' b'
test0 = transform

And the code will work regardless of which overlap you use on the other instance. You don't actually need any pragma on the second instance.

The problem with the original code is that the instances are actually incoherent, not just overlapping, so no combination of {-# OVERLAPS / OVERLAPPING / OVERLAPPABLE #-} would save you - you would need to use {-# INCOHERENT #-}, which is not desirable and I wouldn't recommend it. GHC will tell you about this incoherence with the error message:

>:t transform :: (Transformable a a', Transformable b b') => Either a b -> Either a' b'

<interactive>:1:1: Warning:
    Overlapping instances for Transformable
                                (Either a1 b1) (Either a'1 b'1)
      arising from a use of `transform'
    Matching instances:
      instance [overlappable] (Transformable l l', Transformable r r') =>
                              Transformable (Either l r) (Either l' r')
        -- Defined at test6.hs:9:31
      instance [overlap ok] Transformable a a -- Defined at test6.hs:6:27
    (The choice depends on the instantiation of `a1, b1, a'1, b'1'
     To pick the first instance above, use IncoherentInstances
     when compiling the other instance declarations)
    In the expression:
        transform ::
          (Transformable a a', Transformable b b') =>
          Either a b -> Either a' b'

Essentially, in order to pick from overlapping instances, one instance must be "most specific" for the type(s) you are trying to match. The details of this are given in the user guide.

Upvotes: 12

Related Questions