为什么Fsharp互动允许可变的变量被关闭捕捉?

问题描述 投票:9回答:1

用一个例子来自克里斯·史密斯的编程F#3.0:

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

预期这将失败:

错误FS0407:该可变变量的“x”在无效的方式使用。可变的变量不能被封锁被捕获。

现在,剪切和粘贴功能到FSharp互动的身体:

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

和它的作品!

VAL它:INT = 1

为什么?

f# closures mutable f#-interactive
1个回答
10
投票

编辑:下面的答案是F#高达3.x的正确与F#4.0开始,当地mutables被自动转换成refs如果需要的话,那么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。他们是在堆上分配(而不是在栈上分配的本地可变)所以只要封闭持有对它的引用,也不会被释放。

© www.soinside.com 2019 - 2024. All rights reserved.