Haskell 是否不可能获得可遍历对象中元素的深度?

Haskell 是否不可能获得可遍历对象中元素的深度?,haskell,depth,traversable,Haskell,Depth,Traversable,假设我有这个树类型: {-# LANGUAGE DeriveFoldable, DeriveFunctor #-} data Tree a = Leaf | Branch (Tree a) a (Tree a) deriving(Functor,Foldable) instance Traversable Tree where -- equivalent to the one I could derive, but written out for clarity traverse _ L

假设我有这个
类型:

{-# LANGUAGE DeriveFoldable, DeriveFunctor #-}

data Tree a = Leaf | Branch (Tree a) a (Tree a) deriving(Functor,Foldable)

instance Traversable Tree where -- equivalent to the one I could derive, but written out for clarity
  traverse _ Leaf = pure Leaf
  traverse f (Branch l x r) = Branch <$> traverse f l <*> f x <*> traverse f r
但是要计算任意
可遍历的
中事物的最大深度并不容易。我已经知道光是
函子
是不够的,因为你无法通过
fmap
获得关于它们内部事物“位置”的信息,我也知道光是
可折叠
是不够的,因为
foldr
foldMap
都只提供列表所具有的结构<代码>可遍历
可能是,因为它比
函子
可折叠
更通用

然而,在做了一些实验之后,我认为也没有办法用
Traversable
来实现这一点。到目前为止,我的逻辑是这样的。考虑这两棵树:

fooTree = Branch (Branch Leaf () Leaf) () (Branch Leaf () Leaf)
barTree = Branch (Branch Leaf () (Branch Leaf () Leaf)) () Leaf
现在,
traverse(\()->thingy)fooTree
是:

Branch <$> (Branch <$> pure Leaf <*> thingy <*> pure Leaf) <*> thingy <*> (Branch <$> pure Leaf <*> thingy <*> pure Leaf)
Branch <$> (Branch <$> pure Leaf <*> thingy <*> (Branch <$> pure Leaf <*> thingy <*> pure Leaf)) <*> thingy <*> pure Leaf
在大量使用应用定律并进行一些简化后,即:

(\x y z -> Branch (Branch Leaf x Leaf) y (Branch Leaf z Leaf)) <$> thingy <*> thingy <*> thingy
(\x y z -> Branch (Branch Leaf x (Branch Leaf y Leaf)) z Leaf) <$> thingy <*> thingy <*> thingy
(\x y z->Branch(Branch Leaf x(Branch Leaf y Leaf))z Leaf)thingy thingy
现在,
traverse(\()->thingy)fooTree
traverse(\()->thingy)barTree
看起来它们有着相同的“形状”(唯一的区别是开始时的lambda,甚至它们的类型都是相同的),但它们来自不同深度的树。这让我相信,不可能从
遍历
的角度找到深度,但我不是100%确定,也不确定如何严格解释它


我说这是不可能的,对吗?如果是这样的话,那么如何才能真正严格地解释这一点呢?如果没有,那么您将如何实现它呢?

这确实是不可能的,因为从
可折叠的
可遍历的
实际上没有帮助。获取
树的深度需要合并分支下两个子树的信息。至于

traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
。。。与此相关,任何此类合并只能通过结果的组合应用效果
f
实现(合法的
遍历必须保持
t
结构的形状,并且每个
b
值都是通过
a->fb
函数从单个
a
值获得的)。不过,要获得综合效果

。。。因此,
Traversable
的额外功能在这里没有什么区别

如果仅仅指向
traverse\uu
感觉不够清晰,那么这里有一种替代方法来展示上述论证的最后一步。
transverse
的自然属性之一是Bird等人在年提出的名为“数据类型中的自然性”的属性(详见该论文第6节):

考虑一个任意的
toList
-保留树重排
r::tree a->tree a
,以及一些
f
,使得
遍历f
的结果以某种方式编码树的深度。因为,如上所述,为了计算深度,
fmap(const()),只有组合效应才重要。遍历f
将对深度进行编码,就像遍历f
一样。现在,让我们使用自然主义属性,在两侧组合
fmap(const())

fmap (const ()) . fmap r . traverse f = fmap (const ()) . traverse f . r
-- Or simply:
fmap (const ()) . traverse f = fmap (const ()) . traverse f . r
因为
fmap(const())。遍历f对深度进行编码,这意味着无论是什么,都不会改变树的深度。然而,情况并非如此,例如,如该反例所示:

-- Makes a tree with only leaves as left subtrees, preserving traversal order.
-- Assuming a toList that matches your traverse, or the derived one. 
straighten :: Tree a -> Tree a
straighten = foldr dangle Leaf . toList
    where
    dangle x t = Branch Leaf x t

这是不可能的。考虑深度优先和广度优先,给出一个有效的遍历实现。此外,考虑除了 TraveSabdie之外所需要的,这至少是一些提供的东西,如<代码>控件。
-- r is a natural transformation that preserves toList:
-- toList = toList . r
fmap r . traverse f = traverse f . r
fmap (const ()) . fmap r . traverse f = fmap (const ()) . traverse f . r
-- Or simply:
fmap (const ()) . traverse f = fmap (const ()) . traverse f . r
-- Makes a tree with only leaves as left subtrees, preserving traversal order.
-- Assuming a toList that matches your traverse, or the derived one. 
straighten :: Tree a -> Tree a
straighten = foldr dangle Leaf . toList
    where
    dangle x t = Branch Leaf x t