更新时间:2022-12-23 14:04:05
在浏览GHC手册和Haskell wiki(特别是列表实例页面),我对这是如何工作有了一个更好的想法。以下是我所学到的内容: /www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-770004.3.2rel =noreferrer> Haskell Report 定义了一个像这样的实例声明:
类型( u 1 ... u k )必须采用应用于简单类型变量的类型构造函数 T u 1 ,... u k ;此外, T不能是类型同义词,并且u i 必须全部不同。
以粗体突出显示的部分是限制我的行为。在英语中,它们是:
类型
关键字)来避开规则1。那么这与我的问题有何关系?
[Word16]
只是写作 [] Word16
的另一种方式。换句话说, []
是构造函数, Word16
是它的参数。
所以如果我们试图写:
instance IsString [Word16]
$ b与
相同实例IsString([] Word16)其中...
它不起作用,因为它违反了规则1,正如编译器所指出的那样。
试图将它隐藏在类型的同义词中
type String16 = [Word16]
instance IsString String16 where ...也不管用,因为它违反了第2部分。
因此,不可能得到
[Word16]
(或者任何 列表),以在标准Haskell中实现IsString
。输入...(请滚动)
解决方案#1:
newtype
@ehird建议的解决方案是w在
newtype
中说唱它:newtype String16 = String16 {unString16: :[Word16]}
实例IsString String16其中...它解决了限制因为
String16
不再是别名,它是一种新类型(原谅双关语)!这是唯一的缺点是我们必须手动打包和解包,这很烦人。解决方案#2:灵活的实例
$
$ b{ - #LANGUAGE FlexibleInstances# - }
实例IsString [Word16]其中...这是@ [丹尼尔瓦格纳]建议的解决方案。
(顺便说一下,我最终制作了一个
foldl'
封装在 Data.Text。内部并在其上编写散列。)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
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.
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?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:
Problem
The Haskell Report defines an instance declaration like this:
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:
- Anything after the type constructor must be a type variable.
- 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]
is just another way of writing[] Word16
. In other words,[]
is the constructor andWord16
is its argument.So if we try to write:
instance IsString [Word16]
which is the same as
instance IsString ([] Word16) where ...
it won't work, because it violates rule 1, as the compiler kindly points out.
Trying to hide it in a type synonym with
type String16 = [Word16] instance IsString String16 where ...
won't work either, because it violates part 2.
So as it stands, it is impossible to get
[Word16]
(or a list of anything, for that matter) to implementIsString
in standard Haskell.Enter... (drumroll please)
Solution #1:
newtype
The solution @ehird suggested is to wrap it in a
newtype
:newtype String16 = String16 { unString16 :: [Word16] } instance IsString String16 where ...
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.Solution #2: Flexible instances
At the expense of portability, we can drop the restriction altogether with flexible instances:
{-# LANGUAGE FlexibleInstances #-} instance IsString [Word16] where ...
This was the solution @[Daniel Wagner] suggested.
(By the way, I ended up making a
foldl'
wrapper around Data.Text.Internal and writing the hash on top of that.)