monads中的执行顺序 [英] Order of execution within monads

查看:96
本文介绍了monads中的执行顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何使用State monad,并且我注意到执行顺序方面的一些奇怪行为。删除涉及使用实际状态的干扰位,说我有以下代码:

I was learning how to use the State monad and I noticed some odd behavior in terms of the order of execution. Removing the distracting bits that involve using the actual state, say I have the following code:

import Control.Monad
import Control.Monad.State
import Debug.Trace

mainAction :: State Int ()
mainAction = do
    traceM "Starting the main action"
    forM [0..2] (\i -> do
        traceM $ "i is " ++ show i
        forM [0..2] (\j -> do
            traceM $ "j is " ++ show j
            someSubaction i j
        )
    )

$ b $

在ghci中运行 runState mainAction 1 产生以下输出:

Running runState mainAction 1 in ghci produces the following output:

j is 2          
j is 1          
j is 0          
i is 2          
j is 2          
j is 1          
j is 0          
i is 1         
j is 2          
j is 1          
j is 0          
i is 0          
Outside for loop

好像就像执行可能预期的相反顺序一样。我认为这可能是 forM 的一个怪癖,并且用 sequence 来试用它,它明确指出它按顺序运行它的计算从左到右如下所示:

which seems like the reverse order of execution of what might be expected. I thought that maybe this is a quirk of forM and tried it with sequence which specifically states that it runs its computation sequentially from left to right like so:

mainAction :: State Int ()
mainAction = do
    traceM "Outside for loop"
    sequence $ map handleI [0..2]
    return ()
    where
    handleI i = do
        traceM $ "i is " ++ show i
        sequence $ map (handleJ i) [0..2]
    handleJ i j = do
        traceM $ "j is " ++ show j
        someSubaction i j

然而,序列版本会产生相同的输出。什么是在这里发生的执行顺序的实际逻辑?

However, the sequence version produces the same output. What is the actual logic in terms of the order of execution that is happening here?

推荐答案

Haskell是 lazy 这意味着事情不会立即执行。只要他们的结果是需要的,事情就会被执行 - 但是不会。如果不需要它的结果,代码根本就不会被执行。

Haskell is lazy, which means things are not executed immediately. Things are executed whenever their result is needed – but no sooner. Sometimes code isn't executed at all if its result isn't needed.

如果你粘贴一堆 trace 调用纯函数,你会看到这种懒惰发生。首先需要执行的是首先执行的操作,这是您首先看到的 trace 调用。

If you stick a bunch of trace calls in a pure function, you will see this laziness happening. The first thing that is needed will be executed first, so that's the trace call you see first.

说计算从左到右运行意味着结果与从左到右运行计算结果相同。实际情况下发生的事情可能会非常不同。

When something says "the computation is run from left to right" what it means is that the result will be the same as if the computation was run from left to right. What actually happens under the hood might be very different.

事实上,为什么在纯函数中执行I / O是个坏主意。正如你发现的那样,你会得到怪异的结果,因为执行顺序可以是任何产生正确结果的东西。

This is in fact why it's a bad idea to do I/O inside pure functions. As you have discovered, you get "weird" results because the execution order can be pretty much anything that produces the correct result.

为什么这是个好主意?当语言没有强制执行特定的执行顺序时(比如在命令式语言中看到的传统的从上到下顺序),编译器可以自由地进行大量的优化,例如根本不执行某些代码,因为它的结果是不需要的。

Why is this a good idea? When the language doesn't enforce a specific execution order (such as the traditional "top to bottom" order seen in imperative languages) the compiler is free to do a tonne of optimisations, such as for example not executing some code at all because its result isn't needed.

我会建议你不要在Haskell中考虑过多的执行顺序。应该没有理由。留给编译器。反而想想你想要哪些值。函数是否给出了正确的值?然后它可以工作,不管它执行什么命令。

I would recommend you to not think too much about execution order in Haskell. There should be no reason to. Leave that up to the compiler. Think instead about which values you want. Does the function give the correct value? Then it works, regardless of which order it executes things in.

这篇关于monads中的执行顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆