在Haskell中,我们尝试通过不改变变量或传递参数来以不可变的方式编写大部分代码,而是从旧的代码中创建一个具有所需更改的新值。
main = do
withFile "something.txt" ReadMode (\handle -> do
hSetBuffering handle $ BlockBuffering (Just 2048)
contents <- hGetContents handle
putStr contents)
然后是什么原因是hSetBuffering
,一个接受句柄并设置其缓冲模式的函数,改变传递的handle
本身而不是返回一个具有所需缓冲模式的新句柄?
答案 0 :(得分:6)
使用常规Haskell值,保留旧版本的值没有问题。但是,Handle
是对操作系统分配的可变资源的引用,以及进位状态。在调用返回新hSetBuffering
的{{1}}版本之后,{em>早期版本的Handle
仍会出现什么情况?他们应该反映变化吗?如果答案是肯定的,则Handle
的新句柄返回版本有点谎言。
如果类型系统在调用函数后以某种方式禁止保留hSetBuffering
的旧版本,则此hSetBuffering
的新句柄返回版本可以正常工作。它可以通过强制执行约束来实现:接收Handle
作为参数的函数只能使用该参数一次,而像Handle
这样的“复制”句柄的函数是不允许
有一个(尚未接受的)proposal扩展Haskell,具有强制执行此类限制的能力。事实上,文件操作是一个激励性的例子。从论文的第2.3节开始:
dup :: Handle -> (Handle,Handle)
根据此提案,我们在任何时候都只能拥有type File
openFile :: FilePath → IOL 1 File
readLine :: File ⊸ IOL 1 (File,Unrestricted ByteString)
closeFile :: File ⊸ IOL ω ()
的单个版本。 File
使closeFile
的引用不可用,因此我们无法关闭已经关闭的文件。每个读取操作都采用先前版本的File
并返回一个新的读取数据。 File
的类型如下:
hSetBuffering