如何为结合StateT与IO的GI类型实现带超时的重试生成逻辑?
带有超时重试的StateT IO Monad操作实现
问题背景
我定义了一个自定义类型:
type GI a = StateT GenState IO a
其中GenState是用于生成某种随机树的状态。
在生成树的过程中,无法保证程序能在合理时间内终止,因此我想实现一个带有超时机制的逻辑:若计算超时则终止并重新开始,直到得到结果。
我需要编写如下形式的函数:
tryGeneration :: GI a -> GI a tryGeneraton action = ...
该函数会尝试运行action(超时时间单位为微秒),若超时则从头开始执行action。
注:我是Monad Transformers新手,还没完全理解其原理,尝试用lift结合System.Timeout.timeout但没成功。
已实现的代码(编辑补充)
感谢大家的建议,我已在IO Monad中完成实现:
tryGenerationTime :: Int -> GenState -> GI a -> IO (a, GenState) tryGenerationTime time state action = do (_, s') <- -- change the random state to not generate the same thing over and over res <- timeout time (runStateT action s') case res of Nothing -> tryGenerationTime time s' action Just r -> return r timeItT :: Int -> GI a -> GI a timeItT time action = do state <- get (x, s') <- lift $ tryGenerationTime time state action put s' return x
欢迎提供改进该代码的建议,我只是想快速完成功能,这并非生成问题的最终解决方案,后续还需要限制树的高度才能成功。
代码改进建议
- 补全随机状态更新逻辑:当前代码里的注释行是核心,每次超时重试时必须更新随机状态,否则可能陷入重复生成相同超时树的死循环。如果你的
GenState包含标准随机生成器StdGen,可以这样实现:let (newGen, _) = split (stdGenFromState state) -- 从GenState中取出StdGen并拆分 s' = updateStdGenInState state newGen -- 将新的StdGen放回GenState - 添加重试次数上限:虽然后续会限制树高度,但可以给重试次数加个上限,避免极端情况下的无限递归:
tryGenerationTime :: Int -> Int -> GenState -> GI a -> IO (a, GenState) tryGenerationTime _ 0 _ _ = error "重试次数过多,无法生成有效树" tryGenerationTime time retryLeft state action = do let (newGen, nextGen) = split (stdGenFromState state) s' = updateStdGenInState state newGen res <- timeout time (runStateT action s') case res of Nothing -> tryGenerationTime time (retryLeft - 1) nextGen action Just r -> return r - 优化可读性:给变量起更明确的名字,比如把
time改成timeoutUs标注单位是微秒;把核心逻辑拆分成小辅助函数,让代码结构更清晰。 - 确认线程安全:
timeout通过fork线程实现,要确保GenState的操作是线程安全的——不过这里每次重试都使用新的GenState,风险相对较低。
内容的提问来源于stack exchange,提问作者Ανδρέας Ευαγγελάτος
相关产品推荐
相关产品推荐

