【问题标题】:Are there laws for the Foldable typeclass that constrain how Foldable instances can be derived?Foldable 类型类是否有限制如何派生可折叠实例的法律?
【发布时间】:2020-05-20 14:55:30
【问题描述】:

我正在 Haskell 中试验 Foldable 类型类,使用以下数据类型作为示例:

data Tree a = Empty
            | Node (Tree a) a (Tree a)

如果我使用DeriveFoldable GHC 扩展,它似乎会按照以下方式派生一个Foldable 实例

instance Foldable Tree where
  foldMap _ Empty = mempty
  foldMap f (Node l n r) = (foldMap f l) <> (f n) <> (foldMap f r)

即树的中序遍历。但是,我没有看到任何明显的阻止不同 Foldable 实例的情况,例如前序遍历:

instance Foldable Tree where
  foldMap _ Empty = mempty
  foldMap f (Node l n r) = (f n) <> (foldMap f l) <> (foldMap f r)

Foldable 类型类是否有法律规定前序遍历实例是非法的?

【问题讨论】:

  • 之所以这么推导是因为它使用了参数的顺序,如果你定义为Node a (Tree a) (Tree a),就是前序遍历。
  • 对于仿函数实例,它应该满足foldMap f = fold . fmap f(如文档中所指定)。
  • 我没有看到使用预购遍历定义它的问题。虽然我觉得使用Node a (Tree a) (Tree a) 这样做更有意义,但从那时起,数据结构“暗示”了可折叠的外观。
  • @WillemVanOnsem 所以简而言之,没有任何法律限制如何创建实例,DeriveFoldable 只是使用参数顺序?听起来不错的答案。
  • 嗯,它应该满足一些规律,比如sum = getSum . foldMap Sum等来总结类型。本质上,这个想法是你把它折叠起来,以满足一元法则。

标签: haskell deriving foldable


【解决方案1】:

Foldable 没有指导遍历顺序的规律。实际上,我们可以将编写Foldable 实例的行为视为选择特定的遍历顺序。如果使用DeriveFoldable,则选择遵循类型定义中的字段顺序(另见Daniel Wagner's answer);详细信息记录在the relevant section of the GHC User's Guide

(旁注:正如dfeuer's answer 中所讨论的,Traversable 具有更丰富的规律,其中包括限制可接受的foldMap 实现的范围。不过,您的Tree 的中序和前序遍历都给出了Traversable 的合法实现。)

【讨论】:

    【解决方案2】:

    Foldable本身就很欠缺法律。基本方法是foldMap。预计其他方法的行为类似于它们的默认定义,直至惰性细节。参数化产生了两条规律:

    1. 如果g :: m -&gt; n是一个幺半群态射并且f :: a -&gt; m是一个函数,那么

      g . foldMap f = foldMap (g . f)
      
    2. 如果Foldable 也是Functor,那么对于所有函数g :: b -&gt; mf :: a -&gt; b

      foldMap (g . f) = foldMap g . fmap f
      

    实际上,大多数Foldable 实例也是TraversableTraversabletraverse 有更丰富的法律,并强制执行以下法律

    foldMap f = getConst . traverse (Const . f)
    

    这保证了对于Traversable,容器中的每个元素都恰好折叠一次。而

    instance Foldable [] where
      foldMap _ [] = mempty
      foldMap f (x:_:xs) = f x <> f x <> foldMap f xs
    

    完全有效,没有合法的 Traversable 实例匹配。

    【讨论】:

      【解决方案3】:

      不,没有规定必须以什么顺序访问字段的法律。按照字段在数据结构定义中出现的顺序(或至少是用户可见的 API,如果使用模式同义词)访问字段是常规的),但这只是惯例。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-20
        • 2018-10-18
        相关资源
        最近更新 更多