Why doesn't hSetBuffering return a new handle instead of changing the given handle?

Viewed 120

In Haskell, as we try to write most of our code in immutable way by not changing variables or passed parameters and instead we create a new value from the old one with required changes.

main = do   
withFile "something.txt" ReadMode (\handle -> do  
    hSetBuffering handle $ BlockBuffering (Just 2048)  
    contents <- hGetContents handle  
    putStr contents)  

Then what is the reason than hSetBuffering, a function that takes a handle and sets its buffering mode, changes the passed handle itself instead of returning a new handle with required buffering mode?

1 Answers

With regular Haskell values, there is no problem keeping older versions of a value around. However, Handles are references to mutable resources allocated with the operating system, and carry state. After calling a version of hSetBufferingthat returned a new Handle, what should happen to earlier versions of the Handle that are still kept around? Should they reflect the change? If the answer is yes, then the new-handle-returning version of hSetBuffering is a bit of a lie.

This new-handle-returning version of hSetBuffering could work if the type system somehow disallowed keeping old versions of the Handle after calling the function. It could do that by enforcing a constraint: functions that receive a Handle as parameter can only use that parameter one single time, and functions that "duplicate" handles like dup :: Handle -> (Handle,Handle) are disallowed.

There is a (not yet accepted) proposal to extend Haskell with the ability to enforce such restrictions. In fact, file operations are one of the motivating examples. From section 2.3 of the paper:

type File
openFile :: FilePath → IOL 1 File
readLine :: File ⊸ IOL 1 (File,Unrestricted ByteString)
closeFile :: File ⊸ IOL ω ()

Under this proposal, we can only have a single version of a File around at any given time. closeFile makes the reference to File unavailable so that we can't close an already closed file. Every read operation takes the previous version of the File and returns a new one along with the read data. And hSetBuffering would have a type like:

hSetBuffering :: BufferingMode -> File ⊸ IOL 1 File
Related