有没有办法将这两个不连贯的实例中的一个设置为首选?

时间:2017-09-08 21:00:57

标签: haskell types

我手头有

class C a b where
    f :: b -> a -> b

与此同时,我共存了两个实例

第一个实例的工作原理是逐个应用可折叠集合中的元素:

instance (Foldable m, C a b) => C (m a) b where
    f = foldl f

第二个结合了半群的两个元素:

instance (Monoid a) => C a a where
    f = mappend

当然,类型可以是MonoidFoldable的实例(例如String)。

因为我假设mappend要针对这种情况进行优化(即,定义一种将可折叠集合连接起来的方式比逐个传递元素更有效),我希望编译器尽可能选择第二个实例。我如何表示或强制执行此类偏好?

实际上,要编译代码,我必须将这些实例指定为incoherent,这是正确的。在我看来,C a a更具体,因为它只涉及一种变量类型。

您可以在GitHub上查看代码:kindaro/overlap

2 个答案:

答案 0 :(得分:1)

正如其他人所说,这是一个可怕的想法。但是,我认为只要你不想要任何其他实例,就可能没有IncoherentInstances

instance {-# OVERLAPPABLE #-}
     (Foldable m, C a b, q ~ m a)
   => C q b where
  f = foldl f

instance {-# OVERLAPPING #-}
         Monoid a => C a a where
  f = mappend

对于更可预测的行为,您甚至可以将其重新设置为使用类型系列而不是重叠实例,但这仍然是一个糟糕的主意。这个课程没有任何意义。

*Class Data.Monoid> f (12 :: Sum Int) (ZipList [1,2,3 :: Sum Int])
Sum {getSum = 18}

*Class Data.Monoid> f [1,2,3:: Int] [12::Int]
[1,2,3,12]

答案 1 :(得分:1)

  

我希望编译器尽可能选择第二个实例。

容易。 (但是,是的,您需要非常小心地处理重叠的实例;您绝对不希望Incoherent。)

让我们把首选实例放在首位。

instance (Monoid a) => C a a where
    f = mappend

instance {-# OVERLAPPABLE #-} 
    C2 q b => C q b  where f = f2

class C2 a b where          -- identical to C
    f2 :: b -> a -> b

instance (Foldable m, C a b) => C2 (m a) b where
    f2 = foldl f

这有点类似于@dfeuer的解决方案:放置一个包罗万象的OVERLAPPABLE实例。但是C2的单独约束实例并没有尝试改善类型q(没有~约束),因此您可以自由添加其他实例:只需创建一个包罗万象的C3, C4, ...链。

@dfeuer的评论相反:我建议不要使用INCOHERENT -根据此答案,您始终可以避免使用它。我还建议不要使用OVERLAPPING,而只能使用OVERLAPPABLE。然后,实例作者承认有责任提前解决重叠问题。