3

我有一个玫瑰树结构,我想Traversable为它写一个实例。所以我从以下开始:

data Tree a = Tree a [Tree a] deriving (Show)

instance Functor Tree where
  fmap f (Tree x subs) = Tree (f x) (fmap (fmap f) subs)

我做了它的深度优先变体:

newtype Depth a = Depth (Tree a) deriving (Show)

depth :: Tree a -> [a]
depth (Tree x subs) = x : concatMap depth subs

instance Functor Depth where
  fmap f (Depth t) = Depth $ fmap f t

instance Foldable Depth where
  foldMap f (Depth t) = mconcat $ f <$> depth t

instance Traversable Depth where
  traverse f (Depth t) = Depth <$> go t
    where go (Tree x subs) = Tree <$> f x <*> traverse go subs

然后我尝试了广度优先的变体:

newtype Breadth a = Breadth (Tree a) deriving (Show)

breadth :: Tree a -> [a]
breadth tree = go [tree]
  where
    go [] = []
    go (Tree x subs:q) = x : go (q <> subs)

instance Functor Breadth where
  fmap f (Breadth t) = Breadth $ fmap f t

instance Foldable Breadth where
  foldMap f (Breadth t) = mconcat $ f <$> breadth t

instance Traversable Breadth where
  traverse f (Breadth t) = ???

我意识到这个的广度和深度优先变体Traversable应该是相同的。是这样吗?我不相信我实际上在任何地方都读过这篇文章,但遍历与元素的顺序无关?

如果是这样,这有点奇怪,因为Traversable可以直接实现 for Tree,这意味着Foldable需要实现 for Tree,但显然有多种方式Foldable可以实现。

4

1 回答 1

6

Traversable不得不同意Foldable。具体来说,如果Monoid m,则Applicative (Const m),引起一致性定律foldMap f = getConst . traverse (Const . f)。因此,不可能Breadth共享Depth一个Traversable. Traversable Breadth要么有与它一致的不同实现Foldable,要么根本没有。我可以编写一个我认为确实同意的实现,但我还没有验证其他法律。

instance Traversable Breadth where
  traverse f (Breadth t) = Breadth <$> head <$> go [t]
    where
      go [] = pure []
      go ts = zipWith Tree <$> traverse f rs
                           <*> (fmap (rebuild css) $ go $ concat css)
        where
          (rs, css) = unzip $ map (\(Tree r cs) -> (r, cs)) ts
          -- rebuild s d = evalState (traverse (state splitAt') d) s
          -- I think, but let's keep the dependencies down, shall we?
          rebuild [] [] = []
          rebuild (struct : structs) destruct
            = let (cs, destruct') = splitAt' struct destruct
              in  cs : rebuild structs destruct'
          -- ignoring the as in a [a] makes it look like a number
          splitAt' [] xs = ([], xs)
          splitAt' (_ : n) (x : xs)
            = let (pre, suf) = splitAt' n xs
              in  (x : pre, suf)

这是相当多毛的,到处都是非整体,但它应该可以正常工作。

于 2019-06-10T01:21:23.713 回答