我有一个多参数类型类,它提供了一个有意义的函数,可以将其参数互换:
class Swappable a b where
swappable :: a -> b -> Bool
因此,如果a
和b
形成Swappable a b
,则b
和a
应该形成Swappable b a
。为每个普通实例编写一个交换实例非常麻烦,所以我天真地写了
instance Swappable a b => Swappable b a where
swappable b a = swappable a b
没有编译时出现以下错误:
• Illegal instance declaration for ‘Swappable b a’
(All instance types must be of the form (T a1 ... an)
where a1 ... an are *distinct type variables*,
and each type variable appears at most once in the instance head.
Use FlexibleInstances if you want to disable this.)
• In the instance declaration for ‘Swappable b a’
|
12 | instance Swappable a b => Swappable b a where
| ^^^^^^^^^^^^^
现在,我并不反对打开FlexibleInstances
,但是我不明白为什么我首先需要它。那里的所有类型变量都出现一次,并且都是不同的。那么为什么会出现此错误?
答案 0 :(得分:5)
All instance types must be of the form (T a1 ... an)
意味着您的实例必须采用以下形式
instance Swappable (T a1 .. an) (U b1 .. bn) where ...
其中T
和U
是类型构造函数。如果没有该扩展名,就不能只有一个变量a
和b
,而顶层没有构造函数。
FlexibleInstances
是无害的,并且可以说默认情况下应该将其打开。也许Haskell报告的未来修订版将包含该报告。
相反,我会更担心重叠。 instance Swappable b a => Swappable a b
将与任何其他实例重叠。它还将需要不确定的实例。我不确定您要达到的目标是一个好主意。
答案 1 :(得分:2)
在这种情况下,您可以编写以下内容:
{-# LANGAUGE UndecidableSuperClasses #-} -- 2
class Swappable b a => Swappable a b where -- 1
swappable :: a -> b -> Bool
swappable = flip swappable -- 3
(1)使得,如果您知道a
和b
是Swappable
,那么您也知道b
和a
是Swappable
。也就是说,
swappable' :: Swappable a b => b -> a -> Bool
swappable' = swappable
即使参数顺序似乎是“错误的”,也可以编译,因为Swappable a b
约束本身暗示着Swappable b a
,并且swappable
调用选择的是该实现。
(2)使GHC不再抱怨Swappable
上的递归超类。这是GHC 8.0.1中引入的最新功能。请注意,我们不需要FlexibleInstances
(尽管它是无害的)或UndecidableInstances
(通常是可以的)或OverlappingInstances
(它设置了响亮的喇叭琴)。 UndecidableSuperClasses
可能介于“危险”等级的前两个之间。
(3)是swappable
的默认实现。您可以随心所欲。该类的用法如下:
instance Swappable Bool Int where
swappable _ 0 = False
swappable True n = n > 0
swappable False n = n < 0
instance Swappable Int Bool where -- program does not compile if this is missing
instance Eq a => Swappable a a where swappable = (==)
Swappable Int Bool
类具有Swappable Bool Int
作为超类,因此可以根据该超类来定义其swappable
。 Swappable Bool Int
包含逻辑。如果缺少一个实例,则另一个实例的超类约束将无法实现,因此程序将无法编译。在Swappable a a
情况下,该实例是其自己的“超级实例”。 (它确实需要FlexibleInstances
,但原因与我们的目的正交。)
所有这些工作:
> swappable 5 5 -- defaulted to Integer, mind you
True
> swappable True (5 :: Int)
True
> swappable (5 :: Int) False
False
> swappable' (0 :: Int) True
False