MonoFoldable有什么损失吗?

时间:2016-09-22 08:56:24

标签: haskell foldable classy-prelude

MonoFoldable包中的

mono-traversable似乎能够实现所有常用的可折叠容器等等,例如Bytestring和同类元组之类的东西{{1}但不是MonoFoldable。我的问题是,除了需要一些先进的GHC功能之外,我们是否会丢失Foldable中我们没有的MonoFoldable,除了需要一些高级GHC功能之外,对于实例编写者来说,它会变得更加棘手,并且可能会收到更加错误的错误消息?

例如,是否有一些代码在使用Foldable编译但使用Foldable类型时却没有推断出来?或者使MonoFoldableFoldable更容易使客户端(非实例编写器代码)更简单的任何其他内容?

2 个答案:

答案 0 :(得分:3)

你失去了参数。

类型(Foldable f) => f a -> [a]提供与(MonoFoldable c) => c -> [Element c]明显不同的保证。

您可以使用自由定理生成器来获取属性的一些想法,但作为一个简单示例,前一种类型提供了输出中的任何元素必须出现在输入中的属性。后一种类型无法保证此属性。

答案 1 :(得分:2)

你失去的最大的东西是多态递归。考虑一下Okasaki的catenable列表:

data Cat q a = Empty | Cat a (q (Cat q a))

我们可以写

instance Foldable q => Foldable (Cat q) where
   foldMap _ Empty = mempty
   foldMap f (Cat a q) = f a <> foldMap (foldMap f) q

但如果我们尝试仅使用MonoFoldable,我们就会陷入困境。 qforall x . (MonoFoldable (q x), Element (q x) ~ x)上的必要实例约束无法以任何常规方式表达。可能可以使用Data.Constraint.Forall来解决这个问题,但它会变得相当丑陋。

一个较小的问题是代码可能会获得更复杂的类型签名。例如,

osum :: (MonoFoldable c, Num (Element c)) => c -> Element c

让我不如

sum :: (Foldable f, Num n) => f n -> n

修复很简单:将MonoFoldable的定义更改为

class (a ~ Element c) => MonoFoldable' a c where ...

会给你

osum' :: (MonoFoldable' n c, Num n) => c -> n

或者,完全废弃Element,然后使用

class MonoFoldable'' a c | c -> a

给出了类似的简化签名。

不幸的是,Michael Snoyman在这一点上不同意我的意见。我可以在一段时间内编写自己的包装程序包来公开我的首选API。