Подтвердить что ты не робот

Почему Haskell не допускает синонимы типов при объявлении экземпляров класса?

Хотя я знаю, что существует расширение TypeSynonymInstances в GHC, я понятия не имею, насколько это опасно, и мне интересно, это ограничение произвольно, подобно ограничению мономорфизма, или если для этого есть более глубокие причины.

4b9b3361

Ответ 1

TypeSynonymInstances абсолютно безопасен. Поскольку все, что потенциально похоже на частично применяемые синонимы типов, не разрешено, оно имеет точно такой же эффект, как и в правой части синонима типа в голове экземпляра, т.е.

type Foo a = ([a], [a])
instance Bar (Foo a)

совпадает с

instance Bar ([a], [a])

Однако обратите внимание, что для обоих экземпляров требуется FlexibleInstances, поскольку они содержат вложенные конструкторы типов, а также повторяющиеся переменные типа. В общем, это часто случается после расширения синонимов типов.

Я думаю, это может быть причина, по которой они по умолчанию запрещены.

Однако FlexibleInstances также является совершенно безопасным расширением. Самое худшее, что он может сделать, это вызвать ошибку времени компиляции, если вы попытаетесь определить перекрывающиеся экземпляры, такие как

instance Xyzzy String
instance Xyzzy [a]

Что касается того, почему FlexibleInstances недоступен по умолчанию, я могу только предположить, что он упрощает язык. Стандартные правила, например, главы, гарантируют, что единственные способы определения экземпляров могут быть перекрыты, если конструкторы типов в главах экземпляров идентичны, а FlexibleInstances проверка на перекрытия немного сложнее.

Ответ 2

Как я понимаю, это похоже на ограничение мономорфизма - нет ничего плохого в том, чтобы избавиться от него, но он открывает вам поведение, которого вы не ожидаете. Точно так же, как ограничение мономорфизма ничем не повредит - все типы все еще действительны - это также должно быть абсолютно безопасным: в любом случае существуют ограничения на синонимы типов, которые мешают им делать что-либо более интересное, чем простое сокращение имени (например, вы никогда не сможете частично примените их, поэтому мы не получаем lambdas типа), и поэтому вы всегда можете заменить их правой частью своего определения. Таким образом, поскольку правые части этих определений могут быть проверены в качестве экземпляров (или содержать синонимы других типов для расширения), ничего опасного не должно происходить.

С другой стороны, так же, как отключение ограничения мономорфизма открывает вам потенциально нечетные характеристики производительности, включение экземпляров синтаксиса типов открывает вам ошибки потенциального нечетного типа. Поэтому включите -XTypeSynonymInstances и попробуйте написать экземпляр с синонимом типа:

Prelude> :set -XTypeSynonymInstances
Prelude> instance Num String where (+) = (++)

<interactive>:3:10:
    Illegal instance declaration for `Num String'
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use -XFlexibleInstances if you want to disable this.)
    In the instance declaration for `Num String'

String выглядит как простой старый тип, поэтому это может показаться неожиданным; но это действительно [Char], и поэтому этот экземпляр недействителен в соответствии с строгими правилами Haskell 2010. Если мы расслабляем эти правила, включив -XFlexibleInstances (что, кстати, подразумевает -XTypeSynonymInstances), этот пример работает сейчас:

Prelude> :set -XFlexibleInstances
Prelude> instance Num String where (+) = (++)
... errors about undefined methods ...
Prelude> "a" + "b"
"ab"

Но все становится ужасно быстрым:

Prelude> instance Eq String where
Prelude> "a" == "b"

<interactive>:8:5:
    Overlapping instances for Eq [Char]
      arising from a use of `=='
    Matching instances:
      instance Eq a => Eq [a] -- Defined in `GHC.Classes'
      instance Eq String -- Defined at <interactive>:7:10
    In the expression: "a" == "b"
    In an equation for `it': it = "a" == "b"

Опять же, даже если String выглядит как отдельный тип, у нас уже есть экземпляр для [a], и поэтому это перекрывается с ним. (И на самом деле это, вероятно, является частью того, почему -XFlexibleInstances по умолчанию не включен.) И включение -XOverlappingInstances гораздо более увлекательной идеи, чем включение -XFlexibleInstances.

Ответ 3

Раньше это разрешалось, но в попытке сделать Haskell меньше неожиданностей для новичков, он был запрещен.