为什么我不能使String成为类型类的实例?

时间:2011-05-09 19:45:50

标签: haskell ghc typeclass type-systems

鉴于

data Foo =
  FooString String
  …

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

我想让String成为Fooable的实例:

instance Fooable String where
  toFoo = FooString

GHC然后抱怨:

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'

如果我使用[Char]

instance Fooable [Char] where
  toFoo = FooString

GHC抱怨:

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]'

问题

  • 为什么我不能创建字符串和类型类的实例?
  • 如果我添加一个额外的旗帜,GHC似乎愿意让我逃脱。这是个好主意吗?

4 个答案:

答案 0 :(得分:64)

这是因为String只是[Char]的类型别名,它只是类型构造函数[]在类型Char上的应用,所以这将是形式为([] Char)。其格式不是(T a1 .. an),因为Char不是类型变量。

此限制的原因是为了防止重叠实例。例如,假设您有一个instance Fooable [Char],然后有人出现并定义了instance Fooable [a]。现在,编译器将无法确定您想要使用哪一个,并且会给您一个错误。

通过使用-XFlexibleInstances,您基本上向编译器承诺不会定义任何此类实例。

根据您要完成的任务,定义包装器可能更好:

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

答案 1 :(得分:18)

您遇到了经典Haskell98类型类的两个限制:

  • 他们不允许在实例中使用类型同义词
  • 他们不允许嵌套类型反过来包含类型变量。

这两个语言扩展提升了这些繁重的限制:

  • -XTypeSynonymInstances

允许您使用类型synoyms(例如String代表[Char]),并且:

  • -XFlexibleInstances

解除了对T a b ..形式的实例类型的限制,其中参数是类型变量。 -XFlexibleInstances标志允许实例声明的头部提及任意嵌套类型。

请注意,解除这些限制有时会导致overlapping instances,此时,可能需要额外的语言扩展来解决歧义,允许GHC为您选择一个实例。


参考文献:

答案 2 :(得分:4)

在大多数情况下,FlexibleInstances不是一个好的答案。 更好的替代方法是将String包装在一个newtype中,或者引入一个helper类,如下所示:

class Element a where
   listToFoo :: [a] -> Foo

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo

另请参阅:http://www.haskell.org/haskellwiki/List_instance

答案 3 :(得分:2)

除了这些答案之外,如果您不满意解除限制,可能会出现将String包装成newtype(可以是类的实例)的情况。这种权衡可能是丑陋的,必须包装和解包你的代码。