在 haskell 中实施频道 -- 解决尴尬的小队

Implementing channels in haskell -- Tackling the awkward squad

在论文 Tackling the awkward squad 中,Simon Peyton Jones 提供了 "possible implementation" 的 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)

现在,他实现了这样一个功能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) }

上面的函数从 write 中取出一个 MVar,然后将一个空的 MVar 放入其中。
然后它写入从 write.

中提取的 old_hole

问题是,为什么要写入old_hole?已经从write中取出,作用域仅限于当前块,那有什么区别呢?

The question is, why does it write to old_hole? It has been taken out from write and its scope is limited to the current block only, then what difference does it make?

不完全是。 old_hole 阅读方 上是 "in scope"。全图还得看newChan

newChan = do { 
    read <- newEmptyMVar ;
    write <- newEmptyMVar ;
    hole <- newEmptyMVar ;
    putMVar read hole ;
    putMVar write hole ;
    return (read,write) }

所以在调用 newChan 之后 putChan 中的“old_hole”与 newChan 中的 hole 相同 MVar。随着通道操作的进行,old_hole 总是嵌套在 readMVar 中的某个地方。

我发现链表式通道的设计一开始真的很难理解。该论文中的插图很好地展示了结构,但基本思想是读者 "peel off" 一层 MVar 来显示一个值,而作者插入 MVar 堆的值 "at the bottom",维护指向最底部的指针。

顺便说一句,这是Control.Concurrent.Chan

中使用的设计