Чому я не можу зробити String екземпляром типового класу?


85

Дано :

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 та екземпляр typeclass?
  • GHC, схоже, готовий дозволити мені розібратися з цим, якщо я додаю додатковий прапор. Це гарна ідея?

6
Це питання, які я прихильно ставлю і вважаю улюбленими, бо в іншому випадку я знаю, що найближчим часом я б їх
задав

3
Щодо додаткового прапора: це, мабуть, гарна ідея, якщо ви довіряєте GHC і розумієте, що робить прапор. Yesod спадає на думку: він заохочує вас завжди використовувати прагму OverloadedStrings під час написання додатків Yesod, а QuasiQuotes є необхідністю для правил маршрутизації Yesod. Зауважте, що замість прапора під час компіляції ви також можете помістити {-# LANGUAGE FlexibleInstances #-}(або будь-яку іншу прагму) у верхню частину вашого файлу .hs.
Ден Бертон,

Відповіді:


65

Це тому, що Stringце просто псевдонім типу [Char], який є лише застосуванням конструктора типу []на типі Char, тому це має форму ([] Char). який не має форми, (T a1 .. an)оскільки Charне є змінною типу.

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

Використовуючи -XFlexibleInstances, ви в основному обіцяєте компілятору, що не будете визначати такі випадки.

Залежно від того, що ви намагаєтесь досягти, може бути краще визначити обгортку:

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...

4
Скажімо заради аргументів, що я справді теж хотів instance Fooable [a]. Чи є спосіб змусити toFooфункцію поводитися інакше, якщо aце символ Char?
Джон Ф. Міллер

7
@ John: Існує розширення, -XOverlappingInstancesяке дозволить це, і вибрати найбільш конкретний екземпляр. Детальніше див. У посібнику користувача GHC .
Хаммар

18

Ви стикаєтесь з двома обмеженнями класичних типів класів Haskell98:

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

Ці обтяжливі обмеження скасовано двома розширеннями мови:

  • -XTypeSynonymInstances

що дозволяє використовувати синоніми типу (наприклад, Stringfor [Char]), а також:

  • -XFlexibleInstances

які знімають обмеження на типи екземплярів, що мають форму, T a b ..де параметри є змінними типу. -XFlexibleInstancesПрапор дозволяє главі оголошення примірника згадати довільні вкладені типи.

Зверніть увагу, що скасування цих обмежень іноді може призвести до перекриття екземплярів , і тоді може знадобитися додаткове розширення мови для вирішення двозначності, що дозволяє GHC вибрати екземпляр для вас.


Список літератури ::


4

В більшості випадків гнучкі відстані не є гарною відповіддю. Кращі альтернативи - загортання рядка в новий тип або введення допоміжного класу таким чином:

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.