Когда UndecidableInstances безопасно? Некоторые общие вопросы, касающиеся расширения GHC

Я знаю документацию для -XUndecidableInstances, но я думал, что попрошу проработать.

Предположим, у меня есть два многопараметрических класса (разрешены с -XMultiParamTypeClasses)

class Foo a b
class Goo a b

Теперь предположим, что у меня есть параметризованный тип данных

data Bar a b

который я хочу сделать экземпляром Foo, когда один из его параметров является частью экземпляра Goo. Я не уверен, что предыдущее предложение использует точную терминологию, поэтому вот что я хочу написать:

instance (Goo c d) => Foo d (Bar a d)

Мне не разрешено без расширения UndecidableInstances. Правильно ли я считаю это потому, что экземпляр не относится к типу c?

Должен ли я...

  • Просто разрешите расширение? Может кто-нибудь уточнить, с какими неприятностями это может меня вовлечь?
  • Добавьте еще один параметр в Foo, так что объявление последнего экземпляра станет чем-то вроде Foo c d (Bar a d)? Проблема в том, что у меня могут быть другие экземпляры Foo, которые никогда не ссылаются ни на какой такой "четвертый тип параметра" (т.е. Есть экземпляры формы instance Foo A B в несвязанных частях моего кода), поэтому они ломать. Я бы предпочел исправить свой экземпляр, а не мой класс.
  • Создайте новый класс FooGoo с достаточным количеством параметров? Я бы чувствовал, что я повторяюсь в этом случае, но, по крайней мере, я бы не нарушил несвязанные классы.

Есть ли у кого-нибудь слова мудрости?

Ответ 1

Я правильно понимаю, что это потому, что экземпляр не относится к типу c?

Да, ваш код не придерживается (от здесь):

Для каждого утверждения в контексте: Нет переменная типа имеет больше вхождений в утверждение, чем в голове

В общем, вы должны быть в безопасности, если не добавить другие экземпляры, которые вместе образуют цикл. Когда дело доходит до OverlappingInstances, вы становитесь действительно волосатыми (и зависимыми от компилятора), и, если вы идете на IncoherentInstances, вы правы.

Не зная больше о том, что вы пытаетесь выполнить, трудно дать советы по звуковому дизайну, но первое, что нужно проверить, - действительно ли вам действительно нужно иметь параметр c как параметр для Goo. Вы могли бы выразить то, что вы хотите сделать следующим образом:

class Goo d where
    bar :: d c -> Int
    baz :: Quux c => d c -> Int