F# lazy eval from stream reader?

只谈情不闲聊 提交于 2019-12-10 15:18:45

问题


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.


回答1:


For a quick explanation of what's happening, lets 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 re-write the first two lines of your function as:

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

Next, you've omitted the parentheses on this method:

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

As a result, data has the type unit -> string. When you return this value from your function, the entire result is unit -> 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 of let whatever = ....

With that in mind, here's a fix:

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



回答2:


You don't read from your file. You bind method ReadToEnd of your instance of StreamReader to the value data and then call it when you call getStringFromFile(). The problem is that the stream is closed at this moment.

I think you have missed the parentheses and here's 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


来源:https://stackoverflow.com/questions/3720141/f-lazy-eval-from-stream-reader

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!