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


85

鉴于

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和类型类的实例?
  • 如果我添加一个额外的标志,GHC似乎愿意让我解决这个问题。这是一个好主意吗?

6
我会提出这类问题并将其标记为最喜欢的问题,因为否则我会在不久的将来问到这个问题;)
奥斯卡·梅德罗斯

3
关于额外的标志:只要您信任GHC并了解该标志的作用,这可能是个好主意。想到Yesod:鼓励您在编写Yesod应用程序时始终使用OverloadedStrings编译指示,并且QuasiQuotes是Yesod路由规则的必要条件。请注意,除了在编译时标记外,还可以将{-# LANGUAGE FlexibleInstances #-}(或任何其他编译指示)放在.hs文件的顶部。
丹·伯顿

Answers:


65

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

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

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

根据您要完成的工作,最好定义一个包装器:

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

4
为了争辩,我确实确实也想要instance Fooable [a]toFoo如果a是Char,是否可以使函数的行为有所不同?
约翰·米勒

7
@John:有一个扩展-XOverlappingInstances名将允许这样做,并选择最具体的实例。有关详细信息,请参见GHC用户指南
hammar,2011年

18

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

  • 他们不允许实例中的类型同义词
  • 它们禁止嵌套类型,而嵌套类型又不包含类型变量。

这些繁重的限制通过两种语言扩展得以解除:

  • -XTypeSynonymInstances

它允许你使用类型synoyms(如String[Char]),和:

  • -XFlexibleInstances

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

请注意,解除这些限制有时可能导致实例重叠,这时可能需要附加的语言扩展来解决歧义,从而允许GHC为您选择实例。


参考文献


4

在大多数情况下,FlexibleInstances不是一个好的答案。更好的选择是将String封装为新类型,或引入如下的帮助器类:

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


2

除了这些答案之外,如果您不满意解除限制,则在某些情况下,可以将String包装为新类型,这可以是类的实例。折衷可能是潜在的丑陋,必须包装和解开代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.