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

"Неверное объявление экземпляра" при объявлении экземпляра IsString

Я пишу приложение, которое использует строки UTF-16, и чтобы использовать расширение перегруженных строк, я попытался создать для него экземпляр IsString:

import Data.Word ( Word16 )
import Data.String ( IsString(fromString) )

type String16 = [Word16]

instance IsString [Word16] where
    fromString = encodeUTF16

encodeUTF16 :: String -> String16

Проблема в том, что когда я пытаюсь скомпилировать модуль, GHC 7.0.3 жалуется:

Data/String16.hs:35:10:
    Illegal instance declaration for `IsString [Word16]'
      (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 `IsString [Word16]'

Если я прокомментирую объявление экземпляра, он успешно компилируется.

Почему это отвергается? Экземпляр для [Char] выглядит примерно как одно и то же, но он отлично компилируется. Есть что-то, что я пропустил?

4b9b3361

Ответ 1

После просмотра руководств GHC и вокруг Haskell wiki (особенно Список экземпляров), у меня есть лучшая идея как это работает. Вот краткое изложение того, что я узнал:

Проблема

Haskell Report определяет объявление экземпляра следующим образом:

Тип (T u 1... u k) должен иметь вид конструктор типа T, примененный к простым переменным типа u 1,... u k; кроме того, T не должен быть синонимом типа, а u i должен быть различным.

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

  • Все, что после конструктора типа должно быть переменной типа.
  • Вы не можете использовать псевдоним типа (используя ключевое слово type), чтобы обойти правило 1.

Итак, как это связано с моей проблемой?

[Word16] - это еще один способ записи [] Word16. Другими словами, [] - это конструктор, а Word16 - его аргумент.

Итак, если мы попытаемся написать:

instance IsString [Word16]

что совпадает с

instance IsString ([] Word16) where ...

он не будет работать, потому что он нарушает правило 1, как компилятор любезно указывает.

Попытка скрыть его в синониме типа с

type String16 = [Word16]
instance IsString String16 where ...

тоже не будет работать, потому что он нарушает часть 2.

Таким образом, невозможно получить [Word16] (или список чего угодно, если на то пошло) реализовать IsString в стандартном Haskell.

Введите... (барабаньте пожалуйста)

Решение №1: newtype

Решение @ehird предложило обернуть его в newtype:

newtype String16 = String16 { unString16 :: [Word16] }
instance IsString String16 where ...

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

Решение №2: Гибкие экземпляры

За счет переносимости мы можем полностью отказаться от ограничений с помощью гибких экземпляров:

{-# LANGUAGE FlexibleInstances #-}

instance IsString [Word16] where ...

Это было решение @[Даниэль Вагнер].

(Кстати, я закончил создание обертки foldl' вокруг Data.Text.Internal и написал хэш поверх этого.)

Ответ 2

Почему это отклонено?

Потому что:

  (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.)