声明IsString实例时出现"非法实例声明"

Lam*_*iry 29 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
Run Code Online (Sandbox Code Playgroud)

问题是,当我尝试编译模块时,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]'
Run Code Online (Sandbox Code Playgroud)

如果我注释掉实例声明,它会成功编译.

为什么拒绝这个?该实例[Char]看起来很像同样的事情,但它编译罚款.有没有我错过的东西?

Lam*_*iry 80

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

问题

哈斯克尔报告定义了一个实例声明是这样的:

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

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

  1. 类型构造函数之后的任何内容都必须是类型变量.
  2. 您不能使用类型别名(使用type关键字)来绕过规则1.

那么这与我的问题有何关系?

[Word16]只是另一种写作方式[] Word16.换句话说,[]是构造函数并且Word16是它的参数.

所以如果我们试着写:

instance IsString [Word16]
Run Code Online (Sandbox Code Playgroud)

这是一样的

instance IsString ([] Word16) where ...
Run Code Online (Sandbox Code Playgroud)

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

试图将其隐藏在类型同义词中

type String16 = [Word16]
instance IsString String16 where ...
Run Code Online (Sandbox Code Playgroud)

也不会工作,因为它违反了第2部分.

因此,在标准Haskell中实现它是不可能的[Word16](或者任何事物的列表)IsString.

输入...(鼓请)

解决方案#1: newtype

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

newtype String16 = String16 { unString16 :: [Word16] }
instance IsString String16 where ...
Run Code Online (Sandbox Code Playgroud)

它绕过了限制,因为String16它不再是别名,它是一种新类型(借口双关语)!唯一的缺点是我们必须手动包装和打开它,这很烦人.

解决方案#2:灵活的实例

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

{-# LANGUAGE FlexibleInstances #-}

instance IsString [Word16] where ...
Run Code Online (Sandbox Code Playgroud)

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

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