【问题标题】:Avoid non-exhaustive pattern match when using para recursion-scheme使用 para recursion-scheme 时避免非详尽的模式匹配
【发布时间】:2019-07-15 13:37:14
【问题描述】:

下面是我编写的一些代码,作为使用recursion-schemes 中的para 的练习(我知道这个简化的示例也可以仅使用cata 来解决,但对于这个问题我们忽略它)。

在执行此操作时,我注意到如果我想访问 Depth 构造函数的任何参数的表达式树,我必须在使用 para 时进行非详尽的模式匹配。

我发现gcata'para' 的替代实现没有这个问题,也不需要Comonad,而只需要w 上的Functor 实例。这让我想知道:为什么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 调用本身)

【问题讨论】:

    标签: haskell recursion-schemes


    【解决方案1】:

    para 是一个非常非常特殊的情况。

    值得注意的是,它使用(,) (Mu f) 作为Comonad 的选择。

    这个Comonad 的结构比大多数人都多。

    值得注意的是,它与(,) e -| (-&gt;) e 相邻。

    为什么这很重要? (,) e 保留了 colimits,因此它里面只有一个 a

    所以你可以不用g x &lt;$ k x——因为你只替换了一件东西!

    对于更有趣的Comonad,您的gcata' 应该失败。

    当您在 w a 中替换多个 a 时,您会丢弃信息,因此这不是通用递归方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-06
      • 2021-03-28
      • 2014-09-18
      • 1970-01-01
      • 2020-10-04
      相关资源
      最近更新 更多