Why can I not make String an instance of a typeclass?

Given :

data Foo =
  FooString String
  …

class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo

I want to make String an instance of Fooable :

instance Fooable String where
  toFoo = FooString

GHC then complains:

Illegal instance declaration for `Fooable String'
    (All instance types must be of the form (T t1 ... tn)
     where T is not a synonym.
     Use -XTypeSynonymInstances if you want to disable this.)
In the instance declaration for `Fooable String'

If instead I use [Char] :

instance Fooable [Char] where
  toFoo = FooString

GHC complains:

Illegal instance declaration for `Fooable [Char]'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are 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 `Fooable [Char]'

Question :

  • Why can I not make String and instance of a typeclass?
  • GHC seems willing to let me get away with this if I add an extra flag. Is this a good idea?

  • This is because String is just a type alias for [Char] , which is just the application of the type constructor [] on the type Char , so this would be of the form ([] Char) . which is not of the form (T a1 .. an) because Char is not a type variable.

    The reason for this restriction is to prevent overlapping instances. For example, let's say you had an instance Fooable [Char] , and then someone later came along and defined an instance Fooable [a] . Now the compiler won't be able to figure out which one you wanted to use, and will give you an error.

    By using -XFlexibleInstances , you're basically promising to the compiler that you won't define any such instances.

    Depending on what you're trying to accomplish, it might be better to define a wrapper:

    newtype Wrapper = Wrapper String
    instance Fooable Wrapper where
        ...
    

    You're running into two limitations of classic Haskell98 typeclasses:

  • they disallow type synonyms in instances
  • they disallow nested types that don't in turn contain type variables.
  • These onerous restrictions are lifted by two language extensions:

  • -XTypeSynonymInstances
  • which allows you to use type synoyms (like String for [Char] ), and:

  • -XFlexibleInstances
  • which lift the restrictions on instance types being of the form T ab .. where the parameters are type variables. The -XFlexibleInstances flag allows the head of the instance declaration to mention arbitrary nested types.

    Note that lifting these restrictions can sometimes lead to overlapping instances, at which point, an additional language extension might be needed to resolve the ambiguity, allowing GHC to pick an instance for you.


    References::

  • Relaxed rules for the instance head, in the GHC User's Guide.

  • FlexibleInstances are not a good answer in most cases. Better alternatives are wrapping the String in a newtype or introduce a helper class like so:

    class Element a where
       listToFoo :: [a] -> Foo
    
    instance Element Char where
       listToFoo = FooString
    
    instance Element a => Fooable [a] where
       toFoo = listToFoo
    

    See also: http://www.haskell.org/haskellwiki/List_instance

    链接地址: http://www.djcxy.com/p/43510.html

    上一篇: 意想不到的类型与简单的老分类

    下一篇: 为什么我不能让String成为一个类型类的实例?