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

John F. Miller picture John F. Miller · May 9, 2011 · Viewed 8.2k times · Source

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?

Answer

hammar picture hammar · May 9, 2011

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