I’m running into a bug in my code that makes me think that I don’t really understand some of the details about F# and lazy evaluation. I know that F# evaluates eagerly and therefore am somewhat perplexed by the following function:
// 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
When I call this in 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>.$FSI_0134.main@()
Stopped due to error
This makes me think that getStringFromFile is being evaluated lazily–so I’m totally confused. I’m not getting something about how F# evaluates functions.
For a quick explanation of what’s happening, lets start here:
You can re-write the first two lines of your function as:
Next, you’ve omitted the parentheses on this method:
As a result,
datahas the typeunit -> string. When you return this value from your function, the entire result isunit -> string. But look what happens in between assigning your variable and returning it: you closed you streams.End result, when a user calls the function, the streams are already closed, resulting in the error you’re seeing above.
And don’t forget to dispose your objects by declaring
use whatever = ...instead oflet whatever = ....With that in mind, here’s a fix: