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

匆匆过客 提交于 2019-11-28 16:02:35

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 a b .. 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::

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

Adding to these answers, if you are not comfortable with lifting the restrictions, there may be cases where it could make sense to wrap your String in a newtype, which can be an instance of a class. The tradeoff would be potential ugliness, having to wrap and unwrap in your code.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!