下面是我编写的一些代码,作为使用para
recursion-schemes
的练习(我知道这个简化的示例也可以使用cata
来解决,但让我们忽略它这个问题)。
在执行此操作时,我注意到当我使用para
时,如果我想访问Depth
的任何参数的表达式树,我必须执行非详尽的模式匹配构造
我发现gcata'
和para'
的替代实现没有此问题,也不需要Comonad
,只需要Functor
个实例在w
。这让我想知道:为什么这个版本不用于recursion-schemes
的实现?它有什么问题,还是有更好的方法来实现我正在寻找的东西?
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}
module Test where
import Data.Functor
import Data.Functor.Foldable
data ExprF a = Depth a a -- ^ Counts the maximum depth of the tree
| Unit
deriving Functor
type Expr = Fix ExprF
unit :: Expr
unit = Fix Unit
depth :: Expr -> Expr -> Expr
depth a b = Fix $ Depth a b
evalDepth :: Expr -> Int
evalDepth = cata phi where
phi Unit = 0
phi (Depth a b) = max a b + 1
eval :: Expr -> Int
eval = para phi where
phi Unit = 0
phi (Depth (Fix (Depth a b), _) _) = evalDepth a + evalDepth b
-- ^^^^^^^^^^^^^^^
-- at this point is the whole *current* expression tree, not just
-- the subtree that was given as first argument to `depth`
--------------------------------------------------------------------------------
-- Is this possible without definining gcata' / para' myself with the current API?
eval' :: Expr -> Int
eval' = para' phi where
phi Unit = 0
phi (Depth (a,_) (b,_)) = evalDepth a + evalDepth b
-- ^ ^
-- a and b are just the first and second argument to `depth`. No need
-- to do a pattern match which might go wrong.
gcata' :: forall t w a. (Foldable t, Functor w)
=> (forall b. Base t (w b) -> w (Base t b))
-> (Base t (w a) -> a)
-> t -> a
gcata' k g = fst . c where
c :: t -> (a, w a)
c y = (g x, g x <$ k x) where
x :: Base t (w a)
x = fmap (snd . c) . project $ y
para' :: (Foldable t, Unfoldable t) => (Base t (t,a) -> a) -> t -> a
para' = gcata' distPara
以下是如何使用它的示例:
eval' (depth (depth unit unit) (depth (depth unit unit) unit)
-- 3
eval (depth (depth unit unit) (depth (depth unit unit) unit))
-- 3
正如您所看到的,两个函数都是相同的,计算树的最大深度(不计算最外面的depth
调用本身)
答案 0 :(得分:8)
para
是一个非常特殊的案例。
值得注意的是,它使用(,) (Mu f)
作为Comonad
的选择。
这个Comonad
的结构比大多数都要多。
值得注意的是,它与(,) e -| (->) e
一致。
为什么这很重要?好(,) e
保留了colimits,因此它内部只有一个a
。
所以你可以逃脱g x <$ k x
- 因为你只是替换了一件事!
对于更有趣的Comonad
,您的gcata'
应该失败。
如果在a
中有多个w a
要替换,那么您就会丢弃信息,因此这不会成为一种通用的递归方案。