“Illegal instance declaration” when declaring instance of IsString

后端 未结 2 556
天涯浪人
天涯浪人 2020-12-08 10:16

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:



        
相关标签:
2条回答
  • 2020-12-08 10:16

    Why is this rejected?

    Because:

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

    Is there something I've missed?

    Yes:

       Use -XFlexibleInstances if you want to disable this.)
    
    0 讨论(0)
  • 2020-12-08 10:31

    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.

    Solution #3: Equality constraints

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

    {-# LANGUAGE TypeFamilies #-}
    
    instance (a ~ Word16) => IsString a where ...
    

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

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

    0 讨论(0)
提交回复
热议问题