You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:43:21