2013-05-12 19 views
7

Utilizzando un esempio da programmazione di Chris Smith F # 3.0:Perché Fsharp Interactive consente di catturare le variabili mutabili da chiusure?

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

Questo non riesce come previsto:

errore FS0407: La variabile mutabile 'x' è usato in maniera non valida. Le variabili mutabili non possono essere catturate dalle chiusure.

Ora tagliare e incollare il corpo della funzione in FSharp Interactive:

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

e funziona!

Val IT: int = 1

Perché?

+0

Salve, non genera chiusura quando si esegue solo il codice body.use: let x = ref 0; lasciare incrementX() = x: =! x + 1; vedere Closures: http: //msdn.microsoft.com/en-us/library/dd233186.aspx – kwingho

+0

Si noti che anche il compilatore non si oppone al corpo del funzione da sola. –

risposta

8

Risposta breve: non è a causa di fsi, è perché il mutabile è globale.

Risposta lunga:

Per la cattura normale (non mutabile), implementazione-saggio il valore catturato viene copiato in oggetto funzione, in modo che se si torna questa funzione e utilizzarlo al di fuori del campo di applicazione in cui è stato definito, tutto funziona bene.

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 

D'altra parte, al fine di catturare un mutevole, la cattura deve essere fatto con riferimento, altrimenti non sarebbe in grado di modificarlo. Ma questo è impossibile, perché nel caso precedentemente menzionato in cui la chiusura viene restituita e utilizzata al di fuori del suo ambito di definizione, il mutable è anche fuori portata e potenzialmente deallocato. Questo è il motivo della limitazione iniziale.

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. 

Tuttavia, se il mutabile è globale, non esiste alcun problema di ambito e il compilatore lo accetta. Non è solo fsi; se si tenta di compilare il seguente programma con fsc, funziona:

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! 

In conclusione, come ha detto kwingho, se si vuole avere una chiusura che cattura un valore mutevole locale, utilizzare un ref. Sono allocati all'heap (al contrario della mutabile locale allocata allo stack), quindi finché la chiusura mantiene un riferimento ad esso, non sarà deallocato.