2013-05-12 1 views
7

Используя пример из программирования Криса Смита F # 3,0:Почему Fsharp Interactive позволяет захватывать переменные переменные закрытием?

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

Это не удается, как и ожидалось:

ошибка FS0407: изменяемый переменной «х» используются некорректным способом. Переменные переменные не могут быть зафиксированы закрытием.

Теперь вырезать и вставить тело функции в FSharp Interactive:

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

И это работает!

вал это: INT = 1

Почему?

+0

Привет, он не генерирует замыкание, когда вы просто запускаете код body.use: пусть x = ref 0; пусть incrementX() = x: =! x + 1; см. Закрытие: http: //msdn.microsoft.com/en-us/library/dd233186.aspx – kwingho

+0

Обратите внимание, что компилятор также не возражает против тела функция сама по себе. –

ответ

8

Короткий ответ: это не из-за fsi, это потому, что mutable является глобальным.

Длинный ответ:

Для нормального (не изменяемый) захвата, реализация мудр захваченное значение копируется в объект функции, так, что если вернуть эту функцию и использовать его за пределами области, в которой он был определен, все работает нормально.

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. Они распределены по кучам (в отличие от локального измененного стека), поэтому, если замыкание содержит ссылку на него, оно не будет освобождено.