我有一个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
。我觉得这应该适用于foldlM
或foldrM
。然而,我有点坚持如何做到这一点。那么,我如何编写适用于任意表达式列表的转换的一般规则?
答案 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