RunST阻止访问闭包中另一个有状态线程的引用

时间:2014-06-04 05:14:32

标签: haskell types monads

我遇到过RunST的Rank 2类型的许多解释,以及它如何阻止引用转义RunST。但我无法找出为什么这也会阻止以下代码进行类型检查(这是正确的,但我仍然想知道它是如何做的):

test = do v ← newSTRef True
          let a = runST $ readSTRef v
          return True

与:相比:

test = do v ← newSTRef True
          let a = runST $ newSTRef True >>= λv → readSTRef v
          return True

在我看来,newSTRef的类型对于两种情况都是相同的:Λs. Bool → ST s (STRef s Bool)。如果我理解的话,两种情况下v的类型都是∃s. STRef s Bool。但后来我很困惑接下来要做什么,以显示为什么第一个例子没有进行类型检查,我没有看到两个例子之间的差异,只是在第一个例子v被绑定在runST之外{1}}和第二个内部,我怀疑这是关键。如果我在我的推理中出错,请帮我展示这个和/或纠正我。

1 个答案:

答案 0 :(得分:13)

函数runST :: (forall s . ST s a) -> a是魔术发生的地方。要问的正确问题是如何生成类型为forall s . ST s a的计算。

将其读作英语,这是一个对s的所有选项有效的计算,这是一个幻像变量,表示ST计算的特定“线程”。

当您使用newSTRef :: a -> ST s (STRef s a)时,您正在生成一个STRef,它与生成它的ST线程共享其线程变量。这意味着值s的{​​{1}}类型固定为与较大线程的v类型相同。这种固定性意味着涉及s的操作不再对线程v的所有选择有效。因此,s将拒绝涉及runST的操作。

相反,像v这样的计算在任何newSTRef True >>= \v -> readSTRef v线程中都有意义。整个计算的类型对所有“ST个线程ST都有效”。这意味着它可以使用s运行。

通常,runST必须包围所有runST个线程的创建。这意味着ST的参数中s的所有选择都是任意的。如果runST周围的某些内容与runST的参数相互作用,则可能会修复runST的某些选项以匹配周围的上下文,因此不再可以自由应用所有“s的选择。