用一个例子来自克里斯·史密斯的编程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#高达3.x的正确与F#4.0开始,当地mutables被自动转换成ref
s如果需要的话,那么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
。他们是在堆上分配(而不是在栈上分配的本地可变)所以只要封闭持有对它的引用,也不会被释放。