Грязный взлом на перекрывающиеся экземпляры?

Модуль A импортирует модули B и C

Модуль B импортирует экземпляр X

Модуль C импортирует экземпляр Y

X и Y являются экземплярами класса общего типа.

Экземпляры X и Y идентичны по типу, то есть полностью перекрываются.

Когда я использую B или C по отдельности, все работает. Когда я импортирую их (здесь, в A) для сравнительного тестирования (или тестирования), я получаю ошибку перекрывающихся экземпляров внутри C, говорящую, что оба X и Y совпадают.

Это меня озадачивает, поскольку единственный путь от C к X: C -> A -> B. Так должно быть? Кроме того, есть ли способ обойти это? Я не против грязных хаков, поскольку в обычных обстоятельствах эти два экземпляра никогда не используются вместе. В частности, я не хочу вводить фантомные типы и т. Д.


person letmaik    schedule 18.05.2013    source источник
comment
Вам не нужен путь от C к X, вам нужен путь от C и один от X до общего места встречи. Это здесь А. Экземпляры всегда экспортируются, поэтому в A у вас есть оба экземпляра в области видимости. Единственный разумный способ - не иметь двух экземпляров одного и того же класса для одного и того же типа. (Если B и C оба ваши, исправьте это! Если один ваш, а другой нет, можете ли вы избавиться от своего экземпляра и использовать другой? способ.)   -  person Daniel Fischer    schedule 18.05.2013
comment
Они обе принадлежат мне и представляют собой всего лишь две альтернативные реализации, которые я хочу сохранить. Я знаю, что могу использовать фантомные типы, чтобы обойти это, но, в конце концов, это усложняет ситуацию и в моем случае приводит к более шумному синтаксису (класс типа используется как часть DSL). В Scala можно выбрать, какие экземпляры импортировать, поэтому я подумал, что должен быть какой-нибудь способ.   -  person letmaik    schedule 18.05.2013


Ответы (2)


В зависимости от того, как выглядит ваш класс, это должно быть легко исправить с помощью двух небольших прокси-модулей P и Q, каждый с одинаковой newtype ProxyXY оболочкой с GeneralisedNewtypeDeriving для нужного класса; но один импортирует module B, а другой импортирует module C, поэтому они фактически используют разные экземпляры. Затем у вас есть два типа P.ProxyXY и Q.ProxyXY, которые ведут себя одинаково, но первый использует экземпляр X, а второй - экземпляр Y базового типа.

(Не уверен, что вы имели в виду именно это, говоря «фантомные типы», но на самом деле это нечто иное. )

person leftaroundabout    schedule 18.05.2013
comment
Хм, это класс class Rewritable r a b where..., и я пробовал newtype ProxyXY p = ProxyXY p deriving (Rewritable p), но он говорит: «Невозможно создать производный экземпляр Rewritable p (ProxyXY p)» (даже с хитрым наследованием newtype): «Rewritable p» не имеет арности 1. Между прочим , ваш ответ относится к это? - person letmaik; 18.05.2013
comment
Возможно, вам следовало упомянуть, что вы говорите о классе типа MultiParam! GeneralisedNewtypeDeriving тогда не работает. Вы по-прежнему можете писать новые типы прокси и определять их экземпляры вручную, но это, конечно, будет не очень хорошо. - person leftaroundabout; 19.05.2013

Хорошо, это несерьезный ответ, хотя он позволяет мне скомпилировать код.

Я думаю, что в GHC (7.4.1) или Cabal есть ошибка. Первая компиляция завершается неудачно с указанной ошибкой перекрывающихся экземпляров. Но повторяя это снова (оба раза используя cabal-dev build), он преуспевает! Я объясню почему:

Первая компиляция остановилась во время компиляции C. Итак, C и A не были скомпилированы. При повторном запуске компиляции сначала успешно скомпилирован C, а затем A. Я предполагаю, что C можно скомпилировать сейчас, потому что GHC не получает косвенный экземпляр от B (поскольку C не имеет прямой зависимости от B, а A является скомпилировано после C).

Итак, в конце концов, это проблема, вызванная частичной компиляцией, и действительно очень грязный взлом для моей проблемы.

person letmaik    schedule 18.05.2013