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

29
задан Lambda Fairy 10 July 2014 в 07:38
поделиться

1 ответ

После просмотра руководств 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 и записал хеш поверх этого.)

80
ответ дан 28 November 2019 в 01:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: