我有一个像下面这样的无限结构(使用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没有说“从左到右”,它只谈到“通勤两个仿函数”。
答案 0 :(得分:10)
根据this thread,法律应该是:
traverse Identity == Identity
traverse (Compose . fmap g . f) == Compose . fmap (traverse g) . traverse f
这两个定律确保遍历结构中的每个元素只被遍历一次。哪个顺序无关紧要。您甚至可以说Traversable
实例定义从左到右对于该特定数据类型的含义。
文档中还有另外两个要求:
Functor
实例中,fmap
应该等同于遍历
身份应用仿函数(fmapDefault
)。Foldable
实例中,foldMap
应相当于使用常量应用仿函数(foldMapDefault
)进行遍历。在上面的代码中,您打破了第二个要求,因为您派生的Foldable
将以与Traversable
实例不同的顺序访问元素。使用Foldable
为foldMapDefault
创建自己的实例可以解决此问题。
顺便说一下,sequenceA . fmap (traversepair f)
是traverse (traversepair f)
。