我有一个这样的程序
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
eport,并在 monad 中返回一个新的R
eport,IO
因为select
需要它随机选择一个问题(也因为askQ
将等待用户键盘输入);但是,用户没有选择exit
执行时askQ
,start
会递归调用自身。(fix $ \recurse
这是编写递归 lambda 的技巧。)
上面的代码听起来很像以下几件事:
- monad
State
,或者更恰当地说,monad 转换器,StateT
因为在的递归过程R
中不断演化;start
- 应用组合
forever
器,因为start
是递归的,并且如果用户愿意的话(即,如果他们从不要求退出),可能会永远运行; MaybeT
monad 变压器,因为根据我在实现Maybe
之前读到的内容,实现MonadPlus
应该能够短路。forever
forever
但无法真正判断是否可以使用这些抽象中的任何一个或多个以更惯用的方式编写上述代码,最重要的是避免显式递归。
嗯,结果会是这样的:
这假设
select
和askQ
将被重写以在M
monad 中运行,而不是IO
:结果非常……简洁。
对于后代来说,这是一个独立的例子......
这似乎有效:
因为我最近一直在研究递归方案,所以我想我应该尝试一下单子同态
几乎可以肯定这里还有改进的空间。(在我的脑海中,我相当确定不良报告列表没有被“融合”掉。)