ST monad是如何工作的?

时间:2012-09-17 23:49:58

标签: haskell monads

据我所知,ST monad就像是IO的小兄弟,后者又是添加RealWorld魔法的状态monad。我可以想象状态,我可以想象RealWorld以某种方式放入IO,但每次我写ST的类型签名时,ST monad的s都会让我困惑。

ST s (STArray s a b)为例。 s如何在那里工作?它是否仅用于在计算之间建立一些人为数据依赖性,而不能像状态monad中那样被引用(由于forall)?

我只是抛出想法,真的很感谢比我更有知识的人向我解释。

3 个答案:

答案 0 :(得分:71)

s使ST monad中的对象不会泄漏到ST monad的外部。

-- This is an error... but let's pretend for a moment...
let a = runST $ newSTRef (15 :: Int)
    b = runST $ writeSTRef a 20
    c = runST $ readSTRef a
in b `seq` c

好的,这是一个类型错误(这是一件好事!我们不希望STRef泄漏到原始计算之外!)。由于额外的s,这是一个类型错误。请记住runST有签名:

runST :: (forall s . ST s a) -> a

这意味着您正在运行的计算上的s必须没有约束。因此,当您尝试评估a

a = runST (newSTRef (15 :: Int) :: forall s. ST s (STRef s Int))

结果的类型为STRef s Int,这是错误的,因为s已在forall runST之外“转义”。类型变量总是必须出现在forall的内部,而Haskell允许在任何地方隐式forall量词。根本没有规则允许您有意义地找出a的返回类型。

forall的另一个例子:为了清楚地说明为什么你不允许事情逃脱forall,这里有一个更简单的例子:

f :: (forall a. [a] -> b) -> Bool -> b
f g flag =
  if flag
  then g "abcd"
  else g [1,2]

> :t f length
f length :: Bool -> Int

> :t f id
-- error --

当然f id是一个错误,因为它会返回Char列表或Int列表,具体取决于布尔值是true还是false。这是完全错误的,就像ST的例子一样。

另一方面,如果您没有s类型参数,那么所有内容都会检查就好了,即使代码显然很伪造。

ST实际如何工作:实现方面,ST monad实际上与IO monad相同,但接口略有不同。当您使用ST monad时,您实际上会获得unsafePerformIO或等效的幕后信息。您可以安全地执行此操作的原因是因为所有ST相关函数的类型签名,尤其是具有forall的部分。

答案 1 :(得分:26)

s只是一种破坏,使类型系统阻止你做一些不安全的事情。它在运行时没有“做”任何事情;它只是使类型检查器拒绝执行可疑事情的程序。 (这是一个所谓的幻像类型,只存在于类型检查器的头部,并且在运行时不会影响任何内容。)

答案 2 :(得分:0)

<块引用>

ST s (STArray s a b) 为例:s 在那里是如何工作的?

GHCi 会话时间:

GHCi, version 8.4.3: http://www.haskell.org/ghc/  :? for help
Prelude> let _ = (\x -> x^4) 5 in x

<interactive>:2:27: error: Variable not in scope: x
Prelude> 

范围(或参考框架局部变量 x 是 lambda/匿名函数 \x -> x^4 - 在该范围之外,x 否则未定义,因此出现错误。

runST 的类型:

runST :: (forall s . ST s a) -> a

以类似的方式工作:局部量化类型变量s的范围是类型ST s arunST的参数类型) - 如果 s 出现在结果的类型中,它也超出了它的范围并且也缺乏合理的定义,从而导致类型错误。

所以如果你:

  • 有一个动作m :: ST s (STArray s a b)

  • 而您无意中尝试提取 来自 m 的可变数组:

      … (let arr = runST m in …) …
    

...s转义超出其范围:

forall s . ST s (STArray s a)

(及其由类型系统检测)停止 sharing of mutable state,这是一个众所周知的难以捉摸的错误来源,特别是在并行和并发的上下文中。