ST monad如何工作?
我明白ST monad就像IO的一个小兄弟,反过来也是加入了RealWorld
魔法的状态monad。 我可以描绘出状态,我可以想象RealWorld是以某种方式放入IO中的,但是每次我写ST
的类型签名时,ST monad的s
让我困惑。
以ST s (STArray sab)
为例。 s
在那里工作? 难道仅仅是用来建立计算之间的一些人为的数据依赖而不能像在状态单子(由于各州引用forall
)?
我只是抛出想法,并非常感谢比我更懂事的人向我解释。
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
。
该s
只是一个黑客,使类型系统阻止你做的事情这将是不安全的。 它不会在运行时“做”任何事情; 它只是使类型检查器拒绝程序做出可疑的事情。 (这是一种所谓的幻像类型,只存在于类型检查器的头部,并且在运行时不会影响任何东西。)