2017-11-11 81 views
3

在Haskell中,我们试图通过不改变变量或传递参数来以不可变的方式编写大部分代码,而是通过所需的更改从旧的创建新值。为什么不hSetBuffering返回一个新的句柄,而不是改变给定的句柄?

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

那么是什么原因比hSetBuffering,这需要一个手柄,并将其缓冲模式的功能,改变了传递的handle本身,而不是用所需的缓冲模式返回一个新手柄?

+3

对于您只能读取缓冲区,这可能是好的,但有两个手柄,以具有独立的缓冲器一样可写的对象听起来像疯狂的良方。 –

回答

6

对于常规的Haskell值,保留值的旧版本没有问题。但是,Handle是对操作系统分配的可变资源的引用,以及进位状态。在调用hSetBuffering版本后返回新的Handle版本后,之前版本的Handle版本仍然存在?他们应该反映这种变化吗?如果答案是肯定的,那么hSetBuffering的新句柄返回版本就有点谎言了。

如果类型系统以某种方式不允许在调用该函数后保留旧版本的Handle,那么hSetBuffering的这个新的句柄返回版本可以工作。它可以通过强制执行一个约束来实现:接收Handle作为参数的函数只能使用该参数单一时间,而像dup :: Handle -> (Handle,Handle)这样的“复制”句柄的函数不允许使用

有一个(尚未被接受的)proposal扩展Haskell执行这些限制的能力。事实上,文件操作是其中一个激励性的例子。从本文的第2.3节:

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

根据这项建议,我们只能有一个File的单一版本,在任何给定时间左右。 closeFile使得对File的引用不可用,因此我们无法关闭已经关闭的文件。每个读取操作都采用先前版本的File,并随读取的数据一起返回一个新的操作。而hSetBuffering将有一个类型,如:

hSetBuffering :: BufferingMode -> File ⊸ IOL 1 File 
+0

感谢您的详细解释。 – sidoshi