我知道ST monad有点像IO的小弟弟,而IO则是带有附加RealWorld
魔术的状态monad 。我能想象的状态,我能想象的是现实世界被莫名其妙地投入IO,但每次我写的类型签名ST
中s
的ST单子让我困惑的。
例如,ST s (STArray s a b)
。s
那里的工作如何?它是否只是用来在计算之间建立一些人为的数据依赖性,而不能像状态monad中的状态那样引用(由于forall
)?
我只是抛出一些想法,并且非常感谢比我有更多知识的人向我解释。
我知道ST monad有点像IO的小弟弟,而IO则是带有附加RealWorld
魔术的状态monad 。我能想象的状态,我能想象的是现实世界被莫名其妙地投入IO,但每次我写的类型签名ST
中s
的ST单子让我困惑的。
例如,ST s (STArray s a b)
。s
那里的工作如何?它是否只是用来在计算之间建立一些人为的数据依赖性,而不能像状态monad中的状态那样引用(由于forall
)?
我只是抛出一些想法,并且非常感谢比我有更多知识的人向我解释。
a :: ST Int Int; a = return 2
ST
一个完全普通的状态monad,不同的是,它使用未装箱对作为状态函数的输出,State# s -> (# State# s, a #)
这使它难以处理。runST
尽管ST本身不是一个等级,但这个谜团完全属于2级类型。
Answers:
在s
保持内部的物体ST
泄漏到外部单子ST
单子。
-- 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
,这是错误的,因为ins
已“转义”到forall
in之外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
是一个错误,因为它将根据布尔值是true还是false返回一个listChar
或一个list of Int
。这完全是错误的,就像的示例一样ST
。
另一方面,如果您没有s
type参数,那么即使代码显然是伪造的,所有东西都将进行类型检查。
ST的实际工作方式:在实现方面,ST
monad实际上与IO
monad相同,但接口略有不同。当您使用ST
monad时,您实际上会unsafePerformIO
在幕后得到或等效的结果。之所以可以安全地执行此操作,是因为所有ST
相关功能的类型签名,尤其是带有forall
。的部分。
realWorld#
直接在客户端代码中进行模仿来“破坏” ST的安全性runSTRep
吗?当然,这是一个坏主意,但是我想知道这些保护是什么,除了realworld#
在程序中使用显然是不安全的值之外。 hackage.haskell.org/package/base-4.6.0.1/docs/src/...