Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve overlapping instance

Tags:

haskell

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 ?

like image 432
mb14 Avatar asked Apr 28 '16 11:04

mb14


1 Answers

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.

like image 82
user2407038 Avatar answered Nov 15 '22 20:11

user2407038