首页 文章

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

提问于
浏览
82

Given

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

Question

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

4 回答

  • 4

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

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

    通过使用 -XFlexibleInstances ,您're basically promising to the compiler that you won't定义任何此类实例 .

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

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

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

    • 他们不允许实例中的类型同义词

    • 它们不允许嵌套类型不包含类型变量 .

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

    • -XTypeSynonymInstances

    允许您使用类型synoyms(如 String 用于 [Char] ),以及:

    • -XFlexibleInstances

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

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


    参考文献::

    GHC用户指南中的

  • 18

    在大多数情况下,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

  • 62

    除了这些答案之外,如果您不习惯解除限制,可能会出现将String包装在newtype中的情况,新类型可以是类的实例 . 权衡将是潜在的丑陋,必须包装和解包你的代码 .

相关问题