如何让Haskell中返回IO类型的ed函数实现惰性求值
问题根源
concatM实现的参数顺序错误:foldr的组合函数入参顺序为当前元素 -> 后续折叠结果 -> 最终结果,你当前的实现将入参顺序写反,执行时会先跑完所有后续输入对应的IO操作,再处理当前行的逻辑,必然要等全部输入处理完毕才能返回第一个结果。- 现有主逻辑的执行顺序问题:即使修复
concatM,当前代码会先等待ed返回完整的[String]结果再统一输出,而IO monad的执行是严格的,必须等ed的所有IO操作全部完成才会进入输出步骤,还是无法实现实时输出。
修复方案
方案一:无额外依赖的增量输出(推荐)
不要把所有处理结果攒成一个大的IO [String]再统一输出,改为处理完一段内容就输出一段:
- 先修正
concatM的参数顺序(如果还需要用到这个函数的话):
concatM :: (Foldable t, Monad m) => t (m [a]) -> m [a] concatM xsIO = foldr (\xIO accIO -> do x <- xIO acc <- accIO return $ x ++ acc ) (return []) xsIO
- 修改主函数逻辑,逐步骤处理和输出:
import System.IO (hFlush, stdout) main = do inputLines <- lines <$> getContents let stepActions = map toLinesExt $ scanl (flip $ edLine defHs) (return [Leaf ""]) inputLines forM_ stepActions $ \act -> do resLines <- act putStr $ unlines resLines hFlush stdout -- 强制刷新输出缓冲区,保证内容实时打印
方案二:使用unsafeInterleaveIO实现惰性IO(不推荐)
如果希望尽量保留原有代码结构,可以用unsafeInterleaveIO让IO操作惰性执行,代价是IO执行时机不可控,容易出现资源泄漏或者意外的执行顺序问题:
import System.IO.Unsafe (unsafeInterleaveIO) concatM :: (Foldable t, Monad m) => t (m [a]) -> m [a] concatM xsIO = foldr (\xIO accIO -> do x <- xIO acc <- unsafeInterleaveIO accIO return $ x ++ acc ) (return []) xsIO
修改后原有主函数可以不用调整即可实现实时输出,但只建议在个人脚本等可控场景下使用。
内容的提问来源于stack exchange,提问作者Toorero
相关产品推荐
相关产品推荐

