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

Joh*_*ler 84 haskell type-systems typeclass ghc

鉴于:

data Foo =
  FooString String
  …

class Fooable a where --(is this a good way to name this?)
  toFoo :: a -> Foo
Run Code Online (Sandbox Code Playgroud)

我想做String一个实例Fooable:

instance Fooable String where
  toFoo = FooString
Run Code Online (Sandbox Code Playgroud)

然后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'
Run Code Online (Sandbox Code Playgroud)

如果相反我使用[Char]:

instance Fooable [Char] where
  toFoo = FooString
Run Code Online (Sandbox Code Playgroud)

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]'
Run Code Online (Sandbox Code Playgroud)

问题:

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

ham*_*mar 64

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

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

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

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

newtype Wrapper = Wrapper String
instance Fooable Wrapper where
    ...
Run Code Online (Sandbox Code Playgroud)

  • @John:有一个扩展名为"-XOverlappingInstances"的扩展名允许这样做,并选择最具体的实例.[有关详细信息,请参阅GHC用户指南](http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html#instance-overlap). (7认同)
  • 让我们说为了论证我确实也想要`实例Fooable [a]`.如果`a`是Char,有没有办法让`toFoo`函数表现不同? (4认同)

Don*_*art 18

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

  • 他们在实例中禁止使用类型同义词
  • 它们不允许嵌套类型不包含类型变量.

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

  • -XTypeSynonymInstances

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

  • -XFlexibleInstances

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

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


参考文献:


Lem*_*ing 5

在大多数情况下,FlexibleInstances 并不是一个好的答案。更好的替代方案是将 String 包装在新类型中或引入一个辅助类,如下所示:

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

instance Element Char where
   listToFoo = FooString

instance Element a => Fooable [a] where
   toFoo = listToFoo
Run Code Online (Sandbox Code Playgroud)

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