Почему я не могу сделать String экземпляром класса типов? - PullRequest
83 голосов
/ 09 мая 2011

С учетом

data Foo =
  FooString String
  …

class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo

Я хочу сделать String экземпляром Fooable:

instance Fooable String where
  toFoo = FooString

GHC тогда жалуется:

Illegal instance declaration for `Fooable String'
    (All instance types must be of the form (T t1 ... tn)
     where T is not a synonym.
     Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'

Если вместо этого я использую [Char]:

instance Fooable [Char] where
  toFoo = FooString

GHC жалуется:

Illegal instance declaration for `Fooable [Char]'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are 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 `Fooable [Char]'

Вопрос

  • Почему я не могу сделать String и экземпляр класса типов?
  • GHC, кажется, готов позволить мне сойти с рук, если я добавлю дополнительный флаг. Это хорошая идея?

Ответы [ 4 ]

64 голосов
/ 09 мая 2011

Это потому, что String - это просто псевдоним типа для [Char], который является просто применением конструктора типа [] к типу Char, поэтому он будет иметь вид ([] Char).которая не имеет формы (T a1 .. an), поскольку Char не является переменной типа.

Причина этого ограничения состоит в том, чтобы предотвратить наложение экземпляров.Например, предположим, у вас был instance Fooable [Char], а затем кто-то позже пришел и определил instance Fooable [a].Теперь компилятор не сможет определить, какой из них вы хотите использовать, и выдаст вам ошибку.

Используя -XFlexibleInstances, вы в основном обещаете компилятору, что не будетеопределите любые такие экземпляры.

В зависимости от того, что вы пытаетесь выполнить, может быть лучше определить оболочку:

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...
18 голосов
/ 09 мая 2011

Вы сталкиваетесь с двумя ограничениями классических классов типов Haskell98:

  • они запрещают синонимы типов в случаях
  • они запрещают вложенные типы, которые в свою очередь не содержат переменных типов.

Эти обременительные ограничения снимаются двумя языковыми расширениями:

  • -XTypeSynonymInstances

, что позволяет использовать синонимы типа (например, String для [Char]) и:

  • -XFlexibleInstances

, которые снимают ограничения на типы экземпляров, имеющие форму T a b .., где параметры являются типомпеременные.Флаг -XFlexibleInstances позволяет заголовку объявления экземпляра упоминать произвольные вложенные типы.

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


Ссылки: :

4 голосов
/ 03 марта 2013

FlexibleInstances не являются хорошим ответом в большинстве случаев. Лучшими альтернативами являются завершение String в новый тип или введение вспомогательного класса, например:

class Element a where
   listToFoo :: [a] -> Foo

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo

Смотри также: http://www.haskell.org/haskellwiki/List_instance

2 голосов
/ 10 мая 2011

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

...