Недопустимое объявление экземпляра при объявлении экземпляра 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] выглядит примерно так же, но компилируется нормально. Есть что-то, что я пропустил?


person Lambda Fairy    schedule 26.12.2011    source источник
comment
Вам следует рассмотреть возможность использования текста, который использует внутреннюю кодировку UTF-16. Или, по крайней мере, обертка нового типа вокруг [Word16], чтобы избежать проблем и конфликтов такого рода.   -  person ehird    schedule 26.12.2011
comment
@ehird Спасибо за предложение. Я пытаюсь реализовать функцию хеширования строк Java, которая работает с 16-битными символами. К сожалению, в текстовом пакете нет простого способа работать с необработанным Word16, не прибегая к темной магии.   -  person Lambda Fairy    schedule 27.12.2011
comment
Если вы импортируете Data.Text .Internal, вы можете получить доступ к базовому Массив.   -  person ehird    schedule 27.12.2011
comment
Ну, case s of { Text array offs len -> A.toList array offs len } не так уж и плохо :)   -  person ehird    schedule 27.12.2011
comment
Вы также можете закодируйте его в байтовую строку UTF-16, но это, вероятно, вам не поможет. В любом случае, я бы определенно предложил как минимум newtype в списке.   -  person ehird    schedule 27.12.2011


Ответы (2)


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

Проблема

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

Тип (T u1 … uk) должен иметь форму конструктора типа T применяется к переменным простого типа u1, … uk; кроме того, T не должен быть синонимом типа, а все ui должны быть разными.

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

  1. Все, что следует за конструктором типа, должно быть переменной типа.
  2. Вы не можете использовать псевдоним типа (используя ключевое слово 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 ...

Это решение предложил @[Daniel Wagner].

Решение № 3: Ограничения равенства

Наконец, есть еще менее переносимое решение, использующее ограничения равенства:

{-# LANGUAGE TypeFamilies #-}

instance (a ~ Word16) => IsString [a] where ...

Это лучше работает с выводом типа, но с большей вероятностью перекрывается. См. статью Криса Дона на эту тему.

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

person Lambda Fairy    schedule 29.12.2011

Почему это отвергается?

Так как:

  (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.)
person Daniel Wagner    schedule 26.12.2011
comment
Я уверен, что любой, кто столкнулся бы с этой проблемой (включая меня вчера), понятия не имел, что такое гибкие экземпляры. Не могли бы вы уточнить, что это значит? - person Lambda Fairy; 27.12.2011
comment
FlexibleInstances просто ослабляет ограничение, указанное @DanielWagner; руководство GHC содержит дополнительную информацию на альтернативные ограничения, используемые вместо этого. Это может помочь и вам. - person ehird; 27.12.2011