82

我知道 ST monad 有点像 IO 的小兄弟,而后者又是增加了RealWorld魔法的状态 monad。我可以描绘状态,并且可以描绘 RealWorld 以某种方式被放入 IO,但每次我编写ST monad 的类型签名时都会让我感到困惑STs

举个例子,ST s (STArray s a b)。那里的工作怎么样s?它只是用于在计算之间建立一些人为的数据依赖关系,而不能像状态单子中的状态那样被引用(由于forall)?

我只是提出想法,非常感谢比我更有知识的人向我解释。

4

3 回答 3

79

s保持 monad 内部的对象不会ST泄漏到STmonad 外部。

-- 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已“逃逸”到forallin之外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.

另一方面,如果您没有stype 参数,那么即使代码显然是非常虚假的,一切都可以进行类型检查。

ST的实际工作原理:在实现方面,STmonad 实际上与 monad 相同,IO但接口略有不同。当您在幕后使用ST实际得到的 monad或等效项时。您可以安全地执行此操作的原因是因为所有相关函数unsafePerformIO的类型签名,尤其是带有.STforall

于 2012-09-18T00:11:34.673 回答
28

The s is just a hack that makes the type system stop you doing things which would be unsafe. It doesn't "do" anything at run-time; it just makes the type checker reject programs that do dubious things. (It is a so-called phantom type, a thing with only exists in the type checker's head, and doesn't affect anything at run-time.)

于 2012-09-18T09:18:15.120 回答
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> 

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

的类型runST

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

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

所以如果你:

  • 有行动m :: ST s (STArray s a b)

  • 并且您无意中尝试从以下位置提取可变数组m

      … (let arr = runST m in …) …
    

...逃离s其范围:

forall s . ST s (STArray s a)

(以及类型系统对其进行检测)会停止共享可变状态,这是众所周知的难以捉摸的错误来源,尤其是在并行性和并发性的上下文中。

于 2021-08-06T09:39:37.320 回答