При компиляции моего приложения Haskell с -Wall
опцией GHC жалуется на потерянные экземпляры, например:
Publisher.hs:45:9:
Warning: orphan instance: instance ToSElem Result
Класс типа ToSElem
не мой, он определен HStringTemplate .
Теперь я знаю, как это исправить (переместить объявление экземпляра в модуль, в котором объявлен результат), и я знаю, почему GHC предпочел бы избегать осиротевших экземпляров , но я все еще считаю, что мой способ лучше. Меня не волнует, если компилятор неудобен - скорее это, чем я.
Причина, по которой я хочу объявить свои ToSElem
экземпляры в модуле Publisher, заключается в том, что именно модуль Publisher зависит от HStringTemplate, а не другие модули. Я стараюсь разделять проблемы и избегать зависимости каждого модуля от HStringTemplate.
Я думал, что одно из преимуществ классов типов Haskell по сравнению, например, с интерфейсами Java, состоит в том, что они открыты, а не закрыты, и поэтому экземпляры не нужно объявлять в том же месте, что и тип данных. Похоже, что GHC советует игнорировать это.
Итак, то, что я ищу, - это либо какое-то подтверждение того, что мое мышление правильное и что я могу проигнорировать / подавить это предупреждение, либо более убедительный аргумент против того, чтобы поступать по-моему.
Ответы:
Я понимаю, почему вы хотите это сделать, но, к сожалению, это может быть только иллюзией, что классы Haskell кажутся «открытыми» в том виде, в котором вы говорите. Многие люди считают, что такая возможность является ошибкой в спецификации Haskell, по причинам, которые я объясню ниже. В любом случае, если это действительно не подходит для экземпляра, который вам нужно объявить либо в модуле, где объявлен класс, либо в модуле, где объявлен тип, это, вероятно, знак того, что вы должны использовать
newtype
или какую-либо другую оболочку вокруг вашего типа.Причины, по которым следует избегать бесхозных экземпляров, лежат гораздо глубже, чем удобство компилятора. Эта тема довольно противоречивая, как вы можете видеть из других ответов. Чтобы сбалансировать дискуссию, я собираюсь объяснить точку зрения, согласно которой никогда и никогда не следует писать бесхозные экземпляры, что, на мой взгляд, является мнением большинства опытных хаскеллеров. Мое собственное мнение находится где-то посередине, и я объясню это в конце.
Проблема проистекает из того факта, что когда существует более одного объявления экземпляра для одного и того же класса и типа, в стандартном Haskell нет механизма, чтобы указать, какой из них использовать. Скорее программа отклоняется компилятором.
Самый простой эффект от этого заключается в том, что у вас может быть отлично работающая программа, которая внезапно перестанет компилироваться из-за изменения, которое кто-то внесет в некоторую удаленную зависимость вашего модуля.
Еще хуже то, что работающая программа может начать сбой во время выполнения из-за отдаленного изменения. Вы можете использовать метод, который, как вы предполагаете, исходит из объявления определенного экземпляра, и его можно молча заменить другим экземпляром, который достаточно отличается, чтобы заставить вашу программу начать необъяснимый сбой.
Люди, которым нужны гарантии того, что эти проблемы никогда не возникнут с ними, должны следовать правилу, согласно которому, если кто-либо где-либо когда-либо объявил экземпляр определенного класса для определенного типа, никакой другой экземпляр никогда не должен быть объявлен снова в любой написанной программе. кем угодно. Конечно, есть обходной путь использования a
newtype
для объявления нового экземпляра, но это всегда как минимум незначительное неудобство, а иногда и серьезное. Так что в этом смысле те, кто намеренно пишет экземпляры-сироты, довольно невежливы.Так что же делать с этой проблемой? Лагерь по борьбе с сиротскими экземплярами говорит, что предупреждение GHC - это ошибка, это должна быть ошибка, которая отклоняет любую попытку объявить сиротский экземпляр. А пока мы должны проявлять самодисциплину и избегать их любой ценой.
Как вы видели, есть те, кого эти потенциальные проблемы не слишком беспокоят. Они на самом деле поощряют использование сиротских экземпляров в качестве инструмента для разделения проблем, как вы предлагаете, и говорят, что нужно просто в каждом конкретном случае убедиться, что проблем нет. Я достаточно раз испытывал неудобства из-за чужих сиротских случаев, чтобы убедиться, что такое отношение слишком бесцеремонно.
Я думаю, что правильным решением было бы добавить расширение к механизму импорта Haskell, которое бы контролировало импорт экземпляров. Это не решит проблемы полностью, но окажет некоторую помощь в защите наших программ от ущерба со стороны сиротских экземпляров, которые уже существуют в мире. И затем, со временем, я мог бы убедиться, что в некоторых ограниченных случаях, возможно, сиротский экземпляр может быть не так уж и плох. (И именно это искушение является причиной того, что некоторые в лагере противников сиротства выступают против моего предложения.)
Из всего этого я пришел к выводу, что, по крайней мере, на данный момент, я настоятельно рекомендую вам избегать объявления каких-либо сиротских экземпляров, чтобы быть внимательным к другим, хотя бы по другой причине. Используйте файл
newtype
.источник
Продолжайте и подавите это предупреждение!
Вы в хорошей компании. Конал делает это в "TypeCompose". "chp-mtl" и "chp-transformers" делают это, "control-monad-exception-mtl" и "control-monad-exception-monadsfd" делают это и т. д.
кстати, вы, вероятно, уже знаете это, но для тех, кто этого не делает и наткнулся на свой вопрос при поиске:
{-# OPTIONS_GHC -fno-warn-orphans #-}
Редактировать:
Я признаю проблемы, о которых Иц сказал в своем ответе, как реальные проблемы. Однако я тоже не вижу проблемы в использовании сиротских экземпляров и стараюсь выбрать «наименьшее из всех зол», что, по-моему, разумно использовать сиротские экземпляры.
В своем коротком ответе я использовал только восклицательный знак, потому что ваш вопрос показывает, что вы уже хорошо осведомлены о проблемах. В противном случае я был бы менее восторженным :)
Немного отвлекает, но я считаю, что это идеальное решение в идеальном мире без компромиссов:
Я считаю, что проблемы, о которых упоминает Иц (не зная, какой экземпляр выбран), могут быть решены в "целостной" системе программирования, где:
Вернувшись из фантастического мира (или, надеюсь, будущего), прямо сейчас: я рекомендую стараться избегать бесхозных экземпляров, продолжая использовать их, когда вам "действительно нужно"
источник
Сиротские экземпляры - это неприятность, но, на мой взгляд, они иногда необходимы. Я часто комбинирую библиотеки, в которых тип взят из одной библиотеки, а класс - из другой. Конечно, нельзя ожидать, что авторы этих библиотек предоставят экземпляры для всех мыслимых сочетаний типов и классов. Так что я должен их обеспечивать, и они сироты.
Идея о том, что вам следует обернуть тип новым типом, когда вам нужно предоставить экземпляр, является идеей, имеющей теоретические достоинства, но во многих случаях она слишком утомительна; такая идея выдвигается людьми, которые не зарабатывают на жизнь написанием кода на Haskell. :)
Так что продолжайте и предоставляйте сиротские экземпляры. Они безвредны.
Если вы можете вывести ghc из строя с помощью сиротских экземпляров, то это ошибка, и о ней следует сообщить. (Ошибка ghc, связанная с отсутствием обнаружения нескольких экземпляров, исправить не так уж и сложно.)
Но имейте в виду, что когда-нибудь в будущем кто-то другой может добавить некоторый экземпляр, который уже есть у вас, и вы можете получить ошибку (время компиляции).
источник
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)
использование QuickCheck.В этом случае, я думаю, можно использовать сиротские экземпляры. Общее практическое правило для меня - вы можете определить экземпляр, если вы «владеете» классом типов или если вы «владеете» типом данных (или некоторым его компонентом, т. Е. Экземпляр для Maybe MyData тоже подойдет, по крайней мере, иногда). В рамках этих ограничений вы решаете разместить экземпляр - это ваше личное дело.
Есть еще одно исключение - если вы не владеете ни классом типов, ни типом данных, но создаете двоичный файл, а не библиотеку, то это тоже нормально.
источник
(Я знаю, что опаздываю на вечеринку, но это может быть полезно другим)
Вы можете оставить сиротские экземпляры в своем собственном модуле, тогда, если кто-то импортирует этот модуль, это специально потому, что они им нужны, и они могут избежать их импорта, если они вызывают проблемы.
источник
В соответствии с этим, я понимаю, что такое положение библиотек WRT лагеря анти-сиротских экземпляров, но разве для исполняемых целей не должно хватить сиротских экземпляров?
источник