声明IsString实例时出现“非法实例声明”

时间:2011-12-26 06:28:38

标签: haskell typeclass

我正在编写一个使用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 个答案:

答案 0 :(得分:77)

在浏览了GHC手册和Haskell wiki(特别是List instance页面)后,我对它的工作原理有了更好的了解。以下是我学到的内容摘要:

问题

Haskell Report定义了一个像这样的实例声明:

  

类型( T u 1 ... u k )必须采用类型构造函数 T <的形式/ em>应用于简单类型变量 u 1 ,... u k ;此外, T不能是类型同义词,并且u i 必须都是不同的。

以粗体突出显示的部分是绊倒我的限制。在英语中,它们是:

  1. 类型构造函数之后的任何内容必须是一个类型变量。
  2. 您无法使用类型别名(使用type关键字)来绕过规则1.
  3. 那么这与我的问题有何关系?

    [Word16]只是撰写[] Word16的另一种方式。换句话说,[]是构造函数,Word16是它的参数。

    所以如果我们试着写:

    instance IsString [Word16]
    

    相同
    instance IsString ([] Word16) where ...
    

    它不起作用,因为它违反了规则1,正如编译器所指出的那样。

    尝试使用

    隐藏类型同义词
    type String16 = [Word16]
    instance IsString String16 where ...
    

    也不起作用,因为它违反了第2部分。

    就目前而言,在标准Haskell中实现[Word16]是不可能获得IsString(或任何的列表)。

    输入...(鼓请)

    解决方案#1:newtype

    @ehird建议的解决方案是将其包装在newtype

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

    它解决了限制,因为String16不再是别名,它是一种新类型(请原谅双关语)!唯一的缺点就是我们必须手动包装和打开它,这很烦人。

    解决方案#2:灵活实例

    以便携性为代价,我们可以通过灵活的实例完全放弃限制:

    {-# LANGUAGE FlexibleInstances #-}
    
    instance IsString [Word16] where ...
    

    这是@ [Daniel Wagner]建议的解决方案。

    (顺便说一下,我最终在Data.Text.Internal附近创建了一个foldl'包装器并在其上面写了哈希。)

答案 1 :(得分:-11)

  

为什么会被拒绝?

由于:

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