为什么我不能让String成为一个types类的实例?

鉴于

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

问题

  • 为什么我不能使string和types类的实例?
  • GHC似乎愿意让我摆脱这个,如果我加一个额外的标志。 这是一个好主意吗?

这是因为String只是[Char]一个types别名,它只是typesChar上的types构造函数[]的应用,所以这将是([] Char)的forms。 这不是forms(T a1 .. an)因为Char不是一个typesvariables。

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

通过使用-XFlexibleInstances ,你基本上向编译器保证你不会定义任何这样的实例。

根据你想要完成的事情,定义一个包装可能会更好:

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

你遇到了经典的Haskell98types的两个限制:

  • 它们在实例中不允许types同义词
  • 他们不允许嵌套的types不包含typesvariables。

这些繁重的限制由两种语言扩展解除:

  • -XTypeSynonymInstances

它允许您使用types的synoyms(如[Char] String ),并且:

  • -XFlexibleInstances

这解除了实例types的限制,其forms为T ab ..其中参数是typesvariables。 -XFlexibleInstances标志允许实例声明的头部提及任意的嵌套types。

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


参考文献

  • 在GHC用户指南中放宽实例头的规则 。

在大多数情况下,FlexibleInstances并不是一个好的答案。 更好的替代方法是将string包装成新的types,或者引入一个助手类,如下所示:

 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

除了这些答案之外,如果你不习惯解除这些限制,可能会出现这样的情况:将你的string换成新types,这可能是类的一个实例。 权衡是可能的丑陋,必须在你的代码中打包和解包。