2010-09-15 30 views
4

Je rencontre un bogue dans mon code qui me fait penser que je ne comprends pas vraiment certains détails sur F # et l'évaluation paresseuse. Je sais que F # évalue avec impatience et je suis donc un peu perplexe par la fonction suivante:F # paresseux eval de lecteur de flux?

// Open a file, then read from it. Close the file. return the data. 
let getStringFromFile = 
    File.OpenRead("c:\\eo\\raw.txt") 
    |> fun s -> let r = new StreamReader(s) 
       let data = r.ReadToEnd 
       r.Close() 
       s.Close() 
       data 

Quand j'appelle cela dans FSI:

> let d = getStringFromFile();; 

System.ObjectDisposedException: Cannot read from a closed TextReader. 

at System.IO.__Error.ReaderClosed() 
at System.IO.StreamReader.ReadToEnd() 
at <StartupCode$FSI_0134>[email protected]() 
Stopped due to error 

Cela me fait penser que getStringFromFile est en cours d'évaluation paresseusement - donc Je suis totalement confus. Je ne comprends pas comment F # évalue les fonctions.

Répondre

9

Pour une explication rapide de ce qui se passe, permet de commencer ici:

let getStringFromFile = 
    File.OpenRead("c:\\eo\\raw.txt") 
    |> fun s -> let r = new StreamReader(s) 
       let data = r.ReadToEnd 
       r.Close() 
       s.Close() 
       data 

Vous pouvez ré-écrire les deux premières lignes de votre fonction:

let s = File.OpenRead(@"c:\eo\raw.txt") 

Ensuite, vous avez omis les parenthèses sur cette méthode:

  let data = r.ReadToEnd 
      r.Close() 
      s.Close() 
      data 

en conséquence, data a le type unit -> string. Lorsque vous renvoyez cette valeur à partir de votre fonction, le résultat entier est unit -> string. Mais regardez ce qui se passe entre assigner votre variable et la renvoyer: vous avez fermé vos flux. Résultat final, lorsqu'un utilisateur appelle la fonction, les flux sont déjà fermés, ce qui entraîne l'erreur que vous voyez ci-dessus.

Et n'oubliez pas de disposer de vos objets en déclarant use whatever = ... au lieu de let whatever = ....

Dans cet esprit, voici une solution:

let getStringFromFile() = 
    use s = File.OpenRead(@"c:\eo\raw.txt") 
    use r = new StreamReader(s) 
    r.ReadToEnd() 
+0

+1 excellente explication. Merci pour les conseils. –

2

Vous ne lisez pas de votre dossier. Vous liez la méthode ReadToEnd de votre instance de StreamReader à la valeur data, puis appelez-le lorsque vous appelez getStringFromFile(). Le problème est que le flux est fermé en ce moment.

Je pense que vous avez manqué les parenthèses et voici la version correcte:

// Open a file, then read from it. Close the file. return the data. 
let getStringFromFile = 
    File.OpenRead("c:\\eo\\raw.txt") 
    |> fun s -> let r = new StreamReader(s) 
       let data = r.ReadToEnd() 
       r.Close() 
       s.Close() 
       data