我正在寻找一个可用于跟踪程序进度的monad变换器 . 要解释如何使用它,请考虑以下代码:
procedure :: ProgressT IO ()
procedure = task "Print some lines" 3 $ do
liftIO $ putStrLn "line1"
step
task "Print a complicated line" 2 $ do
liftIO $ putStr "li"
step
liftIO $ putStrLn "ne2"
step
liftIO $ putStrLn "line3"
-- Wraps an action in a task
task :: Monad m
=> String -- Name of task
-> Int -- Number of steps to complete task
-> ProgressT m a -- Action performing the task
-> ProgressT m a
-- Marks one step of the current task as completed
step :: Monad m => ProgressT m ()
我意识到由于monadic定律, step
必须明确存在,并且由于程序确定性/停止问题, task
必须具有明确的步数参数 .
如我所见,上面描述的monad可以通过以下两种方式之一实现:
-
通过一个函数返回当前任务名称/步骤索引堆栈,并在程序中从它停止的位置继续 . 在返回的continuation上重复调用此函数将完成该过程的执行 .
-
通过一个功能,该功能描述了任务步骤完成后要执行的操作 . 该过程将无法控制地运行,直到它完成,通过提供的操作有关更改的环境 .
对于解决方案(1),我使用 Yield
悬架仿函数查看了 Control.Monad.Coroutine
. 对于解决方案(2),我不知道任何已经可用的monad变换器是有用的 .
我正在寻找的解决方案不应该有太多的性能开销,并允许尽可能多地控制过程(例如,不需要IO访问或其他东西) .
这些解决方案中的一个听起来是否可行,或者已经在某个地方解决了这个问题?这个问题是否已经用我无法找到的monad变压器解决了?
EDIT: 目标不是检查是否已执行所有步骤 . 目标是能够在进程运行时"monitor"进程,以便可以判断进程已经完成了多少 .
3 回答
这是我对这个问题的悲观解决方案 . 它使用
Coroutine
来暂停每一步的计算,这允许用户执行任意计算以报告一些进度 .EDIT: 可以找到此解决方案的完整实现here .
Can this solution be improved?
首先,它是如何使用的:
上述方案产出:
实际实现(请参阅注释版本的this):
最明显的方法是使用
StateT
.我不确定你想要
task
的语义是什么,但是......edit to show how you'd do this with IO
请注意,您需要
MonadIO
约束来打印状态 . 如果需要对状态有不同的影响,则可以使用不同类型的约束(例如,如果步数低于零,则抛出异常,或者其他) .不确定这是否正是您想要的,但这是一个强制执行正确步骤数的实现,并要求在最后留下零步骤 . 为简单起见,我使用monad而不是IO上的monad转换器 . 请注意,我没有使用Prelude monad来做我正在做的事情 .
UPDATE :
现在可以提取剩余步骤的数量 . 使用-XRebindableSyntax运行以下命令