我手头有类:
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
当然,类型可以是Monoid
和Foldable
的实例(例如String
)。
因为我假设mappend
要针对这种情况进行优化(即,定义一种将可折叠集合连接起来的方式比逐个传递元素更有效),我希望编译器尽可能选择第二个实例。我如何表示或强制执行此类偏好?
实际上,要编译代码,我必须将这些实例指定为incoherent
,这是正确的。在我看来,C a a
更具体,因为它只涉及一种变量类型。
您可以在GitHub上查看代码:kindaro/overlap。
答案 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
。然后,实例作者承认有责任提前解决重叠问题。