能否用StateT/MaybeT/forever消除该IO动作中的显式递归?
问题描述
我有如下Haskell程序:
start :: [Q] -> R -> IO R start qs = fix $ \recurse r -> do q <- select qs (r', exit) <- askQ q r (if exit then return else recurse) r'
该函数接收问题列表[Q]与报告R,在IO单子中返回新的报告(因为select需要随机选择问题,askQ需等待用户键盘输入);若用户执行askQ时未选择退出,start会递归调用自身。(fix $ \recurse是编写递归lambda的技巧。)
这段代码和以下几个Haskell概念高度契合:
- State单子,或者更合适的StateT单子变换器:因为
R在start的递归过程中不断更新; - forever应用式组合子:因为
start是递归结构,如果用户始终不选择退出,程序可能永久运行; - MaybeT单子变换器:因为
Maybe实现了MonadPlus,它可以让forever实现短路终止。
但我不确定能否用这些抽象更地道地重写上述代码,尤其是消除显式递归。
GHCi实验验证
为了更好地理解已采纳的解决方案,我在GHCi中做了以下实验:
首先定义工具函数:
type M = MaybeT (StateT [String] IO) Int printAndRet rs@(r, s) = putStrLn ("result: " ++ show r ++ ", state: " ++ show s) >> return rs
接着在MaybeT-StateT组合单子中定义4个计算:
c1 = (MaybeT $ StateT $ \s -> printAndRet (Just 1, "again":s)) :: M c2 = (MaybeT $ StateT $ \s -> printAndRet (Just 2, "once more":s)) :: M c3 = (MaybeT $ StateT $ \s -> printAndRet (Nothing, "a final time":s)) :: M c4 = (MaybeT $ StateT $ \s -> printAndRet (Just 10, "and never again":s)) :: M
将它们用>>链式调用并运行,执行命令:
flip runStateT ["some initial state"] $ runMaybeT $ (c1 >> c2 >> c3 >> c4)
输出结果:
result: Just 1, state: ["again","some initial state"] result: Just 2, state: ["once more","again","some initial state"] result: Nothing, state: ["a final time","once more","again","some initial state"] (Nothing,["a final time","once more","again","some initial state"])
内容的提问来源于stack exchange,提问作者Enlico
相关产品推荐
相关产品推荐

