Haskell-具有两个参数的类型类的翻转参数

时间:2018-07-07 08:57:37

标签: haskell typeclass

我有一个多参数类型类,它提供了一个有意义的函数,可以将其参数互换:

class Swappable a b where
    swappable :: a -> b -> Bool

因此,如果ab形成Swappable a b,则ba应该形成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,但是我不明白为什么我首先需要它。那里的所有类型变量都出现一次,并且都是不同的。那么为什么会出现此错误?

2 个答案:

答案 0 :(得分:5)

All instance types must be of the form (T a1 ... an)

意味着您的实例必须采用以下形式

instance Swappable (T a1 .. an) (U b1 .. bn) where ...

其中TU是类型构造函数。如果没有该扩展名,就不能只有一个变量ab,而顶层没有构造函数。

无论如何,

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)使得,如果您知道abSwappable,那么您也知道baSwappable。也就是说,

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作为超类,因此可以根据该超类来定义其swappableSwappable 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