更新 Haskell 中的外部变量

Updating an outer variable in Haskell

从某种意义上说,Haskell 是一种纯函数式语言,当然,惯用代码会尽可能合理地实现函数式语言。同时,Haskell 确实提供了对其他语言熟悉的一些命令式模式的相当直接的翻译支持,例如http://learnyouahaskell.com/a-fistful-of-monads#do-notation

(我知道从某种意义上说,do-notation 'really' 仍然有效;这里的重点是它允许相当直接地转换一些命令式设计模式。)

我感兴趣的一种模式是函数需要更新外部变量,即存在于与其他代码共享的外部作用域中的变量。这可以在 Python:

中简单地证明
def three():
    n = 0

    def inc():
        nonlocal n
        n += 1

    inc()
    inc()
    inc()
    return n

是否有可能在 Haskell 中实现上述设计模式?如果可以,怎么做?

要明确这个问题的范围:

我不是在问 Haskell 中解决上述问题的最佳方法是什么。显然,答案是 three = 3。这只是一个例子。

我不是在问上面的设计模式是好是坏。显然,这将是一个见仁见智的问题。

我不是在问在编写 Haskell 时应该多努力避免使用命令式设计模式。显然,这也是一个见仁见智的问题。

我只是想问问上面的设计模式是否可以在Haskell中实现,如果可以,如何实现。

在Haskell中,您所要做的就是在外部作用域中创建可变变量(实际上是对其的引用)并在内部作用域中使用它。

这里我使用了ST s monad来说明原理,但是你可以用IO和许多其他类型的引用来做同样的事情。

import Control.Monad.ST
import Data.STRef

three :: ST s Int
three = do
   n <- newSTRef 0
   let inc = modifySTRef' n (+1)
   inc
   inc
   inc
   readSTRef n

main :: IO ()
main = print (runST three)   -- output: 3

请注意,在一般情况下,inc 可能是一个大的 do 块,可能会在其范围内创建新的引用(新的局部变量),并拥有自己的内部块.与 python 一样,作用域的嵌套没有限制。