“非法实例声明"声明 IsString 的实例时 [英] "Illegal instance declaration" when declaring instance of IsString

查看:23
本文介绍了“非法实例声明"声明 IsString 的实例时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个使用 UTF-16 字符串的应用程序,为了利用重载的字符串扩展,我尝试为它创建一个 IsString 实例:

I'm writing an application that uses UTF-16 strings, and to make use of the overloaded strings extension I tried to make an IsString instance for it:

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 抱怨:

The problem is, when I try to compile the module, GHC 7.0.3 complains:

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]'

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

If I comment out the instance declaration, it compiles successfully.

为什么会被拒绝?[Char] 的实例看起来非常相似,但它编译得很好.有什么我遗漏的吗?

Why is this rejected? The instance for [Char] looks pretty much like the same thing, yet it compiles fine. Is there something I've missed?

推荐答案

在浏览了 GHC 手册和 Haskell wiki(尤其是 List instance 页面),我对它的工作原理有了更好的了解.以下是我所学到的总结:

After having a look through the GHC manuals and around the Haskell wiki (especially the List instance page), I've got a better idea of how this works. Here's a summary of what I've learned:

Haskell 报告定义了一个实例声明像这样:

The Haskell Report defines an instance declaration like this:

类型(T u1 ... uk)必须采用类型构造函数T 应用于简单类型变量 u1, ... uk;此外,T 不能是类型同义词,并且 ui 必须全部不同.

The type (T u1 … uk) must take the form of a type constructor T applied to simple type variables u1, … uk; furthermore, T must not be a type synonym, and the ui must all be distinct.

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

The parts highlighted in bold are the restrictions that tripped me up. In English, they are:

  1. 类型构造函数之后的任何内容必须是类型变量.
  2. 您不能使用类型别名(使用 type 关键字)来绕过规则 1.
  1. Anything after the type constructor must be a type variable.
  2. You can't use a type alias (using the type keyword) to get around rule 1.

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

So how does this relate to my problem?

[Word16] 只是 [] Word16 的另一种写法.换句话说,[] 是构造函数,Word16 是它的参数.

[Word16] is just another way of writing [] Word16. In other words, [] is the constructor and Word16 is its argument.

所以如果我们尝试写:

instance IsString [Word16]

instance IsString ([] Word16) where ...

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

it won't work, because it violates rule 1, as the compiler kindly points out.

试图将其隐藏在与

type String16 = [Word16]
instance IsString String16 where ...

也不行,因为它违反了第 2 部分.

won't work either, because it violates part 2.

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

So as it stands, it is impossible to get [Word16] (or a list of anything, for that matter) to implement IsString in standard Haskell.

输入...(请打鼓)

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

The solution @ehird suggested is to wrap it in a newtype:

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

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

It gets around the restrictions because String16 is no longer an alias, it's a new type (excuse the pun)! The only downside to this is we then have to wrap and unwrap it manually, which is annoying.

以可移植性为代价,我们可以通过灵活的实例完全取消限制:

At the expense of portability, we can drop the restriction altogether with flexible instances:

{-# LANGUAGE FlexibleInstances #-}

instance IsString [Word16] where ...

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

This was the solution @[Daniel Wagner] suggested.

最后,还有一个使用等式约束的便携性更低的解决方案:

Finally, there's an even less portable solution using equality constraints:

{-# LANGUAGE TypeFamilies #-}

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

这对类型推断更有效,但更有可能重叠.请参阅 Chris Done 关于该主题的文章.

This works better with type inference, but is more likely to overlap. See Chris Done's article on the topic.

(顺便说一句,我最终围绕 Data.Text.Internal 并在其上写入哈希值.)

(By the way, I ended up making a foldl' wrapper around Data.Text.Internal and writing the hash on top of that.)

这篇关于“非法实例声明"声明 IsString 的实例时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆