`instance` для конкретного типа вместо класса типов?

Я определил простой тип списка:

data MyList a = End
               |Entry a (MyList a)

Вместо deriving (Show) я реализовал это явно сам для всех MyList a, где a является экземпляром Show.

instance Show a => Show (MyList a)
  where show End = ""
        show (Entry a l) = (show a) ++","++(show l)

Это прекрасно работает. Теперь я хотел изменить это так, чтобы только MyList String был экземпляром Show, и для этого я написал

instance Show (MyList String)
  where show End = ""
        show (Entry a l) = a ++","++(show l)

Но это привело к ошибке, которую я не понимаю:

Illegal instance declaration for `Show (MyList 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 FlexibleInstances if you want to disable this.)
In the instance declaration for `Show (MyList String)'

Может ли кто-нибудь объяснить, почему это не сработает и о чем мне говорит эта ошибка?


person flawr    schedule 16.06.2017    source источник


Ответы (1)


Ошибка сообщает вам, что стандартный Haskell не допускает таких определений экземпляров. Точно так, как сказано,

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.

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

Наличие этого ограничения на форму типов экземпляров гарантирует, что проверка типов всегда будет завершена, и хотя, конечно, существуют допустимые программы с проверкой типов с экземплярами не этой формы, именно так это и работает: некоторые ограничения в Haskell консервативны.

В вашем конкретном случае просто выполнение того, что предлагает компилятор (т.е. включение FlexibleInstances), сделает трюк, и это расширение безопасно с точки зрения завершения.

person kirelagin    schedule 16.06.2017
comment
Значит, без включения FlexibleInstances я могу внедрить show только для всех классов типов, но не для определенных типов? - person flawr; 16.06.2017
comment
Для конструктора типа (например, вашего MyList :: * -> *) вы можете реализовать классы (например, Show) только для всего «семейства типов» формы MyList a одновременно, а не для конкретных лиц, таких как MyList String или MyList Bool. - person kirelagin; 16.06.2017
comment
Кстати, именно по этой причине Show имеет хакерский _ 2_. Вы хотите отображать списки Char иначе, чем списки всех других типов, но вы не можете просто реализовать отдельный Show [Char] экземпляр в стандартном Haskell. - person kirelagin; 16.06.2017