Вы получаете перекрывающуюся ошибку экземпляров, потому что некоторые из ваших экземпляров P
могут иметь другие экземпляры Show
и тогда компилятор не сможет решить, какие из них использовать. Если у вас есть экземпляр P
для Double
, то вы идете, вы получаете два экземпляра Show
для Double
: ваш общий и тот, который уже объявлен в базовой библиотеке Haskell. Как эта ошибка срабатывает, корректно указывается @augustss в комментариях к вашему вопросу. Для получения дополнительной информации см. the specs.
Как вы уже знаете, нет способа достичь того, что вы пытаетесь без UndecidableInstances
. Когда вы включаете этот флаг, вы должны понимать, что вы берете на себя ответственность компилятора, чтобы гарантировать, что не возникнут конфликтующие экземпляры. Это означает, что, конечно, не должно быть никаких других экземпляров Show
, созданных в вашей библиотеке. Это также означает, что ваша библиотека не будет экспортировать класс P
, что приведет к стиранию возможности библиотеки библиотеки, объявляющей конфликтующие экземпляры.
Если ваш случай каким-то образом противоречит сказанному выше, это надежный признак того, что с ним что-то не так. И на самом деле существует ...
То, что вы пытаетесь достичь, является неправильным, прежде всего. У вас не хватает нескольких важных моментов, о Show
класса типов, отличающие его от конструкций, как toString
метод популярных языков OO:
От Show's haddock:
Результатом шоу является синтаксически правильное выражение Haskell содержащие только константы, с учетом объявлений фиксированности, действующих в точке, где объявлен тип. Он содержит только имена конструкторов, определенные в типе данных, круглых скобках и пробелах.При использовании меток полей конструктора используются также скобки, запятые, имена полей и знаки равенства.
Другими словами, объявление экземпляра Show
, которое не производит действительного выражения Haskell, является само по себе неправильным.
Учитывая вышеизложенное, просто не имеет смысла объявлять пользовательский экземпляр Show
, когда тип позволяет просто получить его.
Если тип не позволяет получить его (например, GADT), обычно вам все равно придется придерживаться экземпляров конкретных типов для получения правильных результатов.
Так что, если вам нужна функция пользовательского представления, вы не должны использовать Show
для этого. Просто объявите пользовательский класс, например:
class Repr a where
repr :: a -> String
и ответьте на заявление об ответственности ответственно.
Перекрытие (и разрешение перегрузки) определяется только головой экземпляра 'Show a', поэтому он действительно перекрывается с любым другим экземпляром Show. – augustss
Предположим, вы объявили экземпляр 'P Int', и у вас уже был экземпляр для' Show Int', поэтому это приведет к перекрытию экземпляров Show. – Satvik
@Satvik Конечно, кроме того, что у меня нет экземпляра 'P Int'. Я ожидал бы ошибку, если бы попытался создать 'P Int', но не просто объявив, что он может существовать. – Impredicative