鉴于:
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]'
问题:
答案 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
答案 3 :(得分:2)
除了这些答案之外,如果您不满意解除限制,可能会出现将String包装成newtype(可以是类的实例)的情况。这种权衡可能是丑陋的,必须包装和解包你的代码。