do 块内的执行顺序真的不依赖于语句顺序吗?



我正在阅读https://wiki.haskell.org/Do_notation_considered_harmful并惊讶地读到以下行

新来的人可能会认为语句的顺序决定了执行的顺序。。。语句的顺序也不是评估顺序的标准。

维基上的文章给出了一些例子来证明这个特性。虽然这些例子有道理,但我仍然不完全相信这个说法是真的,因为如果我写一些类似的东西

main = do
  putStrLn "foo"
  putStrLn "bar"
  putStrLn "baz"

这三行按语句的顺序排列。那么这里到底发生了什么呢?

它说的是语句的顺序不会影响评估标准。正如@chi在IO中指出的那样,monad效应是按顺序排列的,但它们的评估顺序仍然未知。monad的一个例子将使概念变得清晰:

test = do
  x <- Just (2 + undefined)
  y <- Nothing
  return (x + y)

在ghci:中

λ> test
Nothing

上面的代码有三条语句。它可以脱糖成以下形式:

Just (2 + undefined) >>= x -> Nothing >>= y -> return (x + y)

现在,由于(>>=)是关联的,它的评估方式如下:

(Just (2 + undefined) >>= x -> Nothing) >>= y -> return (x + y)

注意,Maybe monad的定义如下:

(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing  >>= _  =  Nothing    -- A failed computation returns Nothing
(Just x) >>= f  =  f x        -- Applies function f to value x

将值(2 + undefined)应用于函数x -> Nothing将产生Nothing。表达式2 + undefined是未求值的,这要归功于Haskell遵循的惰性求值策略。

现在我们有一个简化的形式:

Nothing >>= y -> return (2 + undefined + y)

查看它的Monad实例,您可以看到这将生成Nothing,因为Nothing >>= _ = Nothing。如果论点是严格的呢:

test = do
  !x <- Just (2 + undefined)
  y <- Nothing
  return (y + x)

ghci:演示

λ> test
*** Exception: Prelude.undefined

如果我们遵循严格的评估程序,那么你可以看到顺序实际上很重要。但在懒惰的环境中,语句的顺序并不重要。因此,维基声称,"语句的顺序不是评估顺序的标准"。

相关内容

  • 没有找到相关文章

最新更新