首页 文章

ST monad是如何工作的?

提问于
浏览
64

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

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

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

2 回答

  • 25

    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 ,这是错误的,因为 srunST 中的 forall 之外具有"escaped" . 类型变量总是必须出现在 forall 的内部,而Haskell允许隐含的 forall 量词 . 根本没有规则可以让你有意义地找出 a 的返回类型 .

    Another example with 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 的例子一样 .

    On the other hand, 如果你没有 s 类型参数那么一切都会打字就好了,即使代码显然很伪造 .

    How ST actually works: 实现方面, ST monad实际上与 IO monad相同,但界面略有不同 . 当你使用 ST monad时,你实际上得到 unsafePerformIO 或等效的幕后 . 您可以安全地执行此操作的原因是因为所有 ST 相关函数的类型签名,尤其是具有 forall 的部分 .

  • 69

    s 只是一个黑客,它使类型系统阻止你做一些不安全的事情 . 它在运行时没有任何东西;它只是使类型检查器拒绝执行可疑事情的程序 . (它是一种所谓的幻像类型,只存在于类型检查器中的事物's head, and doesn' t在运行时影响任何东西 . )

相关问题