Traversables真的需要“从左到右”遍历吗?

时间:2013-01-01 12:58:07

标签: haskell

我有一个像下面这样的无限结构(使用Stream包中的streams类型):

data U x = U (Stream x) x (Stream x) deriving (Functor,Foldable)             

我想为它提供一个Traversable实例,如下所示:

instance Traversable U where
    traverse f (U lstream focus rstream) = 
        let pairs =  liftA unzip 
                   . sequenceA . fmap (traversepair f) 
                   $ zip lstream rstream 
            traversepair f (a,b) = (,) <$> f a <*> f b
            rebuild c (u,v) = U u c v
        in rebuild <$> f focus <*> pairs

documentation for Data.Traversable表示它们代表了“可以从左到右遍历的数据结构类”。但是我的定义不是从左到右遍历,而是从向外遍历。在涉及sequence monad的Rand操作之后,我必须以这种方式定义它,以便能够懒散地提取双方的值。

它仍然是一个有效的定义吗?我注意到Typeclassopedia entry for Traversable没有说“从左到右”,它只谈到“通勤两个仿函数”。

1 个答案:

答案 0 :(得分:10)

根据this thread,法律应该是:

  1. traverse Identity == Identity
  2. traverse (Compose . fmap g . f) == Compose . fmap (traverse g) . traverse f
  3. 这两个定律确保遍历结构中的每个元素只被遍历一次。哪个顺序无关紧要。您甚至可以说Traversable实例定义从左到右对于该特定数据类型的含义。

    文档中还有另外两个要求:

    • Functor实例中,fmap应该等同于遍历 身份应用仿函数(fmapDefault)。
    • Foldable实例中,foldMap应相当于使用常量应用仿函数(foldMapDefault)进行遍历。

    在上面的代码中,您打破了第二个要求,因为您派生的Foldable将以与Traversable实例不同的顺序访问元素。使用FoldablefoldMapDefault创建自己的实例可以解决此问题。

    顺便说一下,sequenceA . fmap (traversepair f)traverse (traversepair f)