F # lazy eval from stream reader?

I get an error in my code, which makes me think that I really don’t understand some details about F # and lazy evaluation. I know that F # is looking forward and therefore somewhat puzzled 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 evaluated lazily - so I'm completely confused. I do not understand how F # evaluates functions.

+4
source share
2 answers

For a quick explanation of what is going on, let's start here:

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

You can rewrite the first two lines of your function as:

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

Then you omitted the parentheses by this method:

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

As a result, data is of type unit -> string . When you return this value from your function, the whole result will be unit -> string . But look what happens between the assignment of a variable and its return: you have closed the threads.

The end result, when the user calls the function, the threads are already closed, which leads to the error that you see above.

And don't forget to remove your objects by declaring use whatever = ... instead of let whatever = ...

With that in mind, here's the fix:

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

You are not reading from your file. You bind the ReadToEnd method of your ReadToEnd instance to the data value, and then call it when you call getStringFromFile() . The problem is that the thread is closed at this point.

I think you missed the parentheses and here is the correct version:

 // 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 
+2
source

Source: https://habr.com/ru/post/926002/


All Articles