在项列表上推广monadic表达式

时间:2016-10-16 12:20:20

标签: haskell monads

我有一个monadic函数,可以帮助将Expr类型的值转换为Term#s。签名是

fromDM :: (Name -> CompilerM Term) -> Expr -> CompilerM Term

我总体上无法治疗一个案例。我可以写下每个解决方案的实现

import Control.Monad.State

type CompilerM = State Incr

newtype Incr = Incr Int deriving (Show)

generateNameM :: CompilerM Name
generateNameM = state $ \i ->
  let Incr y = i
      j      = (+1) y
  in  (Name j, Incr j)

data Expr = Tuple [Expr]
data Name = Name Int
data Term = Let Name [Name] Term

fromDM :: (Name -> CompilerM Term) -> Expr -> CompilerM Term
fromDM k expr = case expr of
  Tuple [e1, e2]         -> do
    x  <- generateNameM
    t' <- k x
    fromDM (\z1 -> fromDM (\z2 -> pure $ Let x [z1, z2] t') e2) e1

  Tuple [e1, e2, e3]     -> do
    x  <- generateNameM
    t' <- k x
    fromDM (\z1 -> fromDM (\z2 -> fromDM (\z3 -> pure $ Let x [z1, z2, z3] t') e3) e2) e1

  Tuple [e1, e2, e3, e4] -> do
    x  <- generateNameM
    t' <- k x
    fromDM (\z1 -> fromDM (\z2 -> fromDM (\z3 -> fromDM (\z4 -> return $ Let x [z1, z2, z3, z4] t') e4) e3) e2) e1

现在我想用一般规则替换它,即Tuple es。我觉得这应该适用于foldlMfoldrM。然而,我有点坚持如何做到这一点。那么,我如何编写适用于任意表达式列表的转换的一般规则?

1 个答案:

答案 0 :(得分:2)

好的,我对Cont了解不多,所以让我在谈到这个问题时概述我的思考过程。我想要做的第一件事就是只取出依赖于Tuple内容的代码,因此:

fromDM k expr = case expr of
    Tuple es -> do
        x  <- generateNameM
        t' <- k x
        letExprs x t' es

letExprs x t' [e1, e2] = fromDM (\z1 -> fromDM (\z2 -> pure $ Let x [z1, z2] t') e2) e1
-- etc.

我们想抽象letExprs,以便它可以在任何长度的列表上工作。既然我们已经写下了这个问题,那么Feynman协议的下一步就是思考很难。所以我在不同情况下非常努力地盯着。在我看来,列表中的每个cons单元都变成了对fromDM的调用;在基本情况下,Let被应用于变化列表。我们可以将变量列表放在累加器中,如下所示:

fromDM k expr = case expr of
    Tuple es -> do
        x  <- generateNameM
        t' <- k x
        letExprs x t' [] es

letExprs x t' vars [] = pure $ Let x (reverse vars) t'
letExprs x t' vars (e:es) = fromDM (\z -> letExprs x t' (z:vars) es) e

这对我来说已经很不错了。如果你想把它变成一个折叠(由于通常的原因这很好:你不会意外地搞砸了递归模式,读者知道你没有做任何棘手的事情),我们现在几乎可以阅读它直接关闭:

fromDM k expr = case expr of
    Tuple es -> do
        x  <- generateNameM
        t' <- k x
        foldr (\e k vars -> fromDM (\z -> k (z:vars)) e)
              (\vars -> pure $ Let x (reverse vars) t')
              es