在解决尴尬的小队一文中,西蒙·佩顿·琼斯 (Simon Peyton Jones) 提供了Channel
.
type Channel a = (MVar (Stream a) , -- Read end
MVar (Stream a) ) -- Write end (the hole)
type Stream a = MVar (Item a)
data Item a = MkItem a (Stream a)
Run Code Online (Sandbox Code Playgroud)
现在,他实现了putChan :: Channel a -> a -> IO ()
这样的功能
putChan (read, write) val
= do { new_hole <- newEmptyVar ;
old_hole <- takeMVar write ;
putMVar write new_hole ;
putMVar old_hole (MkItem val new_hole) }
Run Code Online (Sandbox Code Playgroud)
上面的函数从写入中取出一个 MVar,然后将一个空的 MVar 放入其中。
然后它写入从 write 中提取的 old_hole。
问题是,为什么要写入old_hole?已经从write中取出,作用域仅限于当前块,那有什么区别呢?
问题是,为什么要写入old_hole?它已经从 write 中取出了,并且它的作用域仅限于当前块,那有什么区别呢?
不完全的。old_hole
在读取端“在范围内” 。你必须看看newChan
完整的图片:
newChan = do {
read <- newEmptyMVar ;
write <- newEmptyMVar ;
hole <- newEmptyMVar ;
putMVar read hole ;
putMVar write hole ;
return (read,write) }
Run Code Online (Sandbox Code Playgroud)
因此,在调用newChan
“ old_hole
”之后,与中的putChan
相同。随着通道操作的进展,它总是嵌套在的 s 中的某个地方。MVar
hole
newChan
old_hole
MVar
read
我发现链表式通道的设计一开始确实很难理解。该论文中的插图很好地展示了结构,但基本思想是读者“剥离”一层 MVar 以显示一个值,而作者则在 MVar 堆的“底部”插入值,以维护指向最底层的指针。
顺便说一句,这是Control.Concurrent.Chan中使用的设计