我有这个 F 代数(在上一个问题中介绍过),我想在它上面加上一个有效的代数。通过绝望的试验,我设法拼凑出一个有效的一元变态。我想知道它是否可以推广到应用程序,如果不能,为什么。
这就是我定义的方式Traversable
:
instance Traversable Expr where
traverse f (Branch xs) = fmap Branch $ traverse f xs
traverse f (Leaf i ) = pure $ Leaf i
这是一元变态:
type AlgebraM a f b = a b -> f b
cataM :: (Monad f, Traversable a) => AlgebraM a f b -> Fix a -> f b
cataM f x = f =<< (traverse (cataM f) . unFix $ x)
这就是它的工作原理:
λ let printAndReturn x = print x >> pure x
λ cataM (printAndReturn . evalSum) $ branch [branch [leaf 1, leaf 2], leaf 3]
1
2
3
3
6
6
我现在的想法是我可以这样重写:
cataA :: (Applicative f, Traversable a) => AlgebraM a f b -> Fix a -> f b
cataA f x = do
subtree <- traverse (cataA f) . unFix $ x
value <- f subtree
return value
不幸的是,value
这取决于subtree
并且根据一篇关于 applicative do-notation 的论文,在这种情况下,我们不能对 Applicative 脱糖。似乎没有办法解决这个问题;我们需要一个单子从嵌套的深处浮起来。
这是真的吗?我可以安全地得出结论,只有扁平结构可以折叠,仅具有应用效果吗?