据我了解,Haskell 的 ST monad 是独一无二的,因为它允许我描述一个本地使用可变内存的计算,但仍然呈现一个纯接口。也就是说,只要该突变不会逃脱计算,它就允许我对内存进行突变。最简单的例子是这样的:
runST $ newSTRef "trying to return a memory reference."
这会导致编译时错误(太酷了!):
Couldn't match type ‘a’ with ‘STRef s a0’
because type variable ‘s’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context: ST s a
at <interactive>:1:1-18
Expected type: ST s a
Actual type: ST s (STRef s a0)
In the second argument of ‘($)’, namely ‘newSTRef 0’
In the expression: runST $ newSTRef 0
我的理解是,如果允许可变引用逃避计算,它可能会在其他地方发生突变,并且我的 ST 计算可能不再具有纯接口(它可能在不同的调用上返回不同的值)。
我的问题是:这是如何实现的?我看到 runST 和 newSTRef 的定义大量使用了 RankNTypes,但我无法理解这些类型是如何产生上述编译错误的。