Как разрешить перекрывающийся экземпляр

у меня есть следующий код (преобразование, аналогичное преобразование)

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

конечно, эти экземпляры перекрываются в случае, когда я пытаюсь преобразовать Either a b to Either a b и получаю следующее сообщение об ошибке (ParsingError - это псевдоним для типа 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

я попробовал другую комбинацию OVERLAPS, OVERLAPPING и OVERLAPPABLE но ничего не работает. Как я могу это решить ?

1 ответов


вам придется изменить одно из определений примеру:

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

и код будет работать независимо от того, какое перекрытие вы используете в другом экземпляре. На самом деле вам не нужна никакая ПРАГМА во втором экземпляре.

проблема с исходным кодом заключается в том, что таких случаев на самом деле некогерентного, не просто перекрывается, поэтому нет комбинации {-# OVERLAPS/OVERLAPPING/OVERLAPPABLE #-} спасет вас - вам нужно будет использовать {-# INHCOHERENT #-}, что нежелательно, и я бы не рекомендовать. GHC расскажет вам об этой несогласованности в сообщении об ошибке:

>: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'

по существу, чтобы выбрать из перекрывающихся экземпляров, один экземпляр должен быть "наиболее конкретным" для типа(ов), который вы пытаетесь сопоставить. Подробности этого приведены в руководство пользователя.