«Недопустимое объявление экземпляра» при объявлении экземпляра IsString - PullRequest
28 голосов
/ 26 декабря 2011

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

Ответы [ 2 ]

77 голосов
/ 29 декабря 2011

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

Проблема

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

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

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

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

Это было решение, предложенное [Даниэлем Вагнером].

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

0 голосов
/ 26 декабря 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.)
...