7

mono-traversable包中的MonoFoldable似乎能够实现所有常见的可折叠容器等等,例如,Bytestring可以制作同质元组之类的东西,MonoFoldable但不能Foldable。我的问题是,除了需要一些高级 GHC 功能之外,我们是否会丢失任何我们MonoFoldable没有的东西,这使得它对于例如编写者来说稍微有点棘手,并且可能会得到更丑陋的错误消息?Foldable

例如,是否有一些代码在使用Foldable编译时MonoFoldable没有推断出类型?或者其他任何使客户端(不是实例编写器代码)比比简单得多的Foldable东西MonoFoldable

4

2 回答 2

4

你失去了参数化。

A 类型(Foldable f) => f a -> [a]提供的保证与(MonoFoldable c) => c -> [Element c].

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

于 2016-09-29T19:48:42.423 回答
4

你失去的最大的东西是多态递归。考虑 Okasaki 的可连接列表:

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

但是如果我们尝试使用 just MonoFoldable,我们就会陷入困境。q, ,上的必要实例约束forall 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。


更新:现在我们有了QuantifiedConstraints语言扩展,实际上可以FoldableMonoFoldable!

于 2016-09-29T20:51:41.727 回答