用于进度跟踪的 Monad 变压器
我正在寻找一个可用于跟踪程序进度的 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 ()
我意识到由于一元法则,step
必须明确存在,并且task
必须有一个明确的步骤由于程序确定性/停止问题,数字参数。
正如我所看到的,上面描述的 monad 可以通过以下两种方式之一实现:
- 通过一个返回当前任务名称/步骤索引堆栈的函数,以及过程中停止点的延续。在返回的延续上重复调用此函数将完成过程的执行。
- 通过一个函数,该函数执行一个描述任务步骤完成后要执行的操作的函数。该过程将不受控制地运行,直到完成为止,通过提供的操作“通知”环境有关更改。
对于解决方案 (1),我使用 Yield
悬挂函子查看了 Control.Monad.Coroutine
。对于解决方案(2),我不知道有任何可用的 monad 转换器是有用的。
我正在寻找的解决方案不应该有太多的性能开销,并允许尽可能多地控制过程(例如不需要 IO 访问或其他东西)。
这些解决方案之一听起来可行吗?或者是否已经有其他解决方案可以解决此问题?这个问题是否已经通过我找不到的 monad 转换器解决了?
编辑:目标不是检查是否已执行所有步骤。目标是能够在进程运行时“监视”该进程,以便人们可以知道它已经完成了多少。
I am looking for a monad transformer that can be used to track the progress of a procedure. To explain how it would be used, consider the following code:
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 ()
I realize that step
has to exist explicitly because of the monadic laws, and that task
has to have an explicit step number parameter because of program determinism/the halting problem.
The monad as described above could, as I see it, be implemented in one of two ways:
- Via a function that would return the current task name/step index stack, and a continuation in the procedure at the point that it left off. Calling this function repeatedly on the returned continuation would complete the execution of the procedure.
- Via a function that took an action describing what to do when a task step has been completed. The procedure would run uncontrollably until it completed, "notifying" the environment about changes via the provided action.
For solution (1), I have looked at Control.Monad.Coroutine
with the Yield
suspension functor. For solution (2), I don't know of any already available monad transformers that would be useful.
The solution I'm looking for should not have too much performance overhead and allow as much control over the procedure as possible (e.g. not require IO access or something).
Do one of these solutions sound viable, or are there other solutions to this problem somewhere already? Has this problem already been solved with a monad transformer that I've been unable to find?
EDIT: The goal isn't to check whether all the steps have been performed. The goal is to be able to "monitor" the process while it is running, so that one can tell how much of it has been completed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是我对这个问题的悲观解决方案。它使用协程来暂停每一步的计算,这让用户可以执行任意计算来报告一些进度。
编辑:此解决方案的完整实现可以在此处找到。
这个解决方案可以改进吗?
首先,它是如何使用的:
上面的程序输出:
实际的实现(参见this 用于评论版本):
This is my pessimistic solution to this problem. It uses
Coroutine
s to suspend the computation on each step, which lets the user perform an arbitrary computation to report some progress.EDIT: The full implementation of this solution can be found here.
Can this solution be improved?
First, how it is used:
The above program outputs:
The actual implementation (See this for a commented version):
最明显的方法是使用 StateT。
我不确定您想要
task
的语义是什么,但是...编辑以显示如何使用 IO 执行此操作
请注意,您需要用于打印状态的
MonadIO
约束。如果您需要对状态产生不同的效果(即,如果步骤数低于零或其他情况,则抛出异常),您可以有不同类型的约束。The most obvious way to do this is with
StateT
.I'm not sure what you want the semantics of
task
to be, however...edit to show how you'd do this with IO
Note that you'll need the
MonadIO
constraint to print the state. You can have a different sort of constraint if you need a different effect with the state (i.e. throw an exception if the number of steps goes below zero, or whatever).不确定这是否正是您想要的,但这里有一个实现,它强制执行正确的步骤数,并要求最后剩下零步骤。为简单起见,我使用 monad 而不是 IO 上的 monad 转换器。请注意,我没有使用 Prelude monad 来做我正在做的事情。
更新:
现在可以提取剩余步数。使用 -XRebindableSyntax 运行以下命令
Not sure if this is exactly what you want, but here is an implementation that enforces the correct number of steps and requires there to be zero steps left at the end. For simplicity, I'm using a monad instead of a monad transformer over IO. Note that I am not using the Prelude monad to do what I'm doing.
UPDATE:
Now can extract the number of remaining steps. Run the following with -XRebindableSyntax