f# - 为什么Fsharp Interactive允许可变变量被闭包捕获?

标签 f# closures mutable f#-interactive

使用Chris Smith的Programming F#3.0中的示例:

let invalidUseOfMutable() =
    let mutable x = 0
    let incrementX() = x <- x + 1
    incrementX()
    x;;

这将按预期失败:

error FS0407: The mutable variable 'x' is used in an invalid way. Mutable variables cannot be captured by closures.



现在,将函数的主体剪切并粘贴到FSharp Interactive中:
let mutable x = 0
let incrementX() = x <- x + 1
incrementX()
x;;

而且有效!

val it : int = 1



为什么?

最佳答案

编辑:以下答案对于3.x以下的F#是正确的。从F#4.0开始,如果需要,本地可变变量会自动转换为ref,因此OP的代码实际上在所有情况下都可以成功编译。

简短的答案:不是因为fsi,而是因为可变的是全局的。

长答案:

对于常规(非可变)捕获,将实现值复制到函数对象中,这样,如果您返回此函数并在已定义范围之外使用它,则一切正常。

let pureAddOne() =
    let x = 1
    let f y = x + y    // the value 1 is copied into the function object
    f

let g = pureAddOne()
g 3    // x is now out of scope, but its value has been copied and can be used

另一方面,为了捕获可变变量,需要通过引用完成捕获,否则您将无法对其进行修改。但这是不可能的,因为在前面提到的关闭返回并在其定义范围之外使用的情况下,该可变变量也超出了范围,并可能被释放。这就是最初限制的原因。
let mutableAddOne() =
    let mutable x = 1
    let f y = x <- x + y    // x would be referenced, not copied
    f

let g = mutableAddOne()
g 3    // x is now out of scope, so the reference is invalid!
       // mutableAddOne doesn't compile, because if it did, then this would fail.

但是,如果可变变量是全局变量,则不会出现这种范围问题,编译器会接受它。不只是fsi;如果您尝试使用fsc编译以下程序,它将起作用:
module Working

let mutable x = 1    // x is global, so it never goes out of scope

let mutableAddOne() =
    let f y = x <- x + y    // referencing a global. No problem!
    f

let g = mutableAddOne()
g 3    // works as expected!

总结,正如kwingho所说,如果您想要一个闭包来捕获局部可变值,请使用ref。它们是堆分配的(与堆栈分配的本地可变对象相反),只要闭包持有对它的引用,就不会将其释放。

关于f# - 为什么Fsharp Interactive允许可变变量被闭包捕获?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16509587/

相关文章:

f# - 你如何将这棵可变树转换成一棵不可变树?

c# - 可变 F# 记录的二进制序列化

asp.net - F# 中的 SignalR 和 SelfHost

c# - 为什么 F# 比 C# 慢这么多? (质数基准)

types - 与文字的可区分联合

迭代数组与列表的性能

javascript - 是否可以创建一个可以将另一个函数(也有参数)作为参数的 throttle 函数,以及时间延迟

javascript - 使用循环和闭包设置元素的 innerHTML

arrays - 平面嵌套对象的闭包?

rust - 如何在可变迭代器上实现drop_while?