s
保持 monad 内部的对象不会ST
泄漏到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))
结果将具有 type STRef s Int
,这是错误的,因为s
已“逃逸”到forall
in之外runST
。类型变量总是必须出现在 a 的内部forall
,并且 Haskell 允许在任何地方使用隐式forall
量词。根本没有规则可以让您有意义地找出a
.
另一个例子forall
:为了清楚地说明为什么你不能让事情逃脱 a 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
这完全是错误的,就像ST
.
另一方面,如果您没有s
type 参数,那么即使代码显然是非常虚假的,一切都可以进行类型检查。
ST的实际工作原理:在实现方面,ST
monad 实际上与 monad 相同,IO
但接口略有不同。当您在幕后使用ST
实际得到的 monad或等效项时。您可以安全地执行此操作的原因是因为所有相关函数unsafePerformIO
的类型签名,尤其是带有.ST
forall