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

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

问题描述

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

  import Data.Word(Word16)
import Data.String(IsString(fromString))

类型String16 = [Word16]

实例IsString [Word16]其中
fromString = encodeUTF16

encodeUTF16 :: String - > String16

问题是,当我尝试编译模块时,GHC 7.0.3抱怨:数据/ String16.hs:35:10:
'IsString [Word16]'的非法实例声明'


,其中a1 ... an是*不同类型变量*,
,并且每个类型变量在实例头中至多出现一次。
如果你想禁用它,使用-XFlexibleInstances。)
在`IsString [Word16]'的实例声明中

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



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

解决方案

在浏览GHC手册和Haskell wiki(特别是<一个href =http://www.haskell.org/haskellwiki/List_instance =noreferrer>列表实例页面),我对这是如何工作有了一个更好的想法。以下是我所学到的内容: /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. 类型构造函数之后的任何内容都必须是类型变量。

  2. 无法使用类型别名(使用类型关键字)来避开规则1。

  3. ol>

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

    [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:

    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] is just another way of writing [] Word16. In other words, [] is the constructor and Word16 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 implement IsString 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.)

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

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