imz*_*hev 11 io haskell ghc state-monad
在GHC.IO中,差异和预期用途有何用途ioToST和unsafeSTToIO定义?
-- ---------------------------------------------------------------------------
-- Coercions between IO and ST
-- | A monad transformer embedding strict state transformers in the 'IO'
-- monad. The 'RealWorld' parameter indicates that the internal state
-- used by the 'ST' computation is a special one supplied by the 'IO'
-- monad, and thus distinct from those used by invocations of 'runST'.
stToIO :: ST RealWorld a -> IO a
stToIO (ST m) = IO m
ioToST :: IO a -> ST RealWorld a
ioToST (IO m) = (ST m)
-- This relies on IO and ST having the same representation modulo the
-- constraint on the type of the state
--
unsafeIOToST :: IO a -> ST s a
unsafeIOToST (IO io) = ST $ \ s -> (unsafeCoerce# io) s
unsafeSTToIO :: ST s a -> IO a
unsafeSTToIO (ST m) = IO (unsafeCoerce# m)
Run Code Online (Sandbox Code Playgroud)
Seb*_*edl 12
安全版本必须在IO monad中启动(因为您无法获取ST RealWorldfrom runST)并允许您在IO上下文和ST RealWorld上下文之间切换.它们是安全的,因为ST RealWorld它与IO基本相同.
不安全的版本可以从任何地方开始(因为runST可以在任何地方调用)并允许您在任意ST monad和IO monad之间切换.使用runST纯粹的上下文然后unsafeIOToST在状态monad中进行操作基本上等同于使用unsafePerformIO.
Mat*_*hid 10
TL; DR.所有这四个函数都只是类型转换.它们在运行时都是无操作的.它们之间的唯一区别是类型签名 - 但它是首先强制执行所有安全保证的类型签名!
该ST单子和IO单子都为您提供可变的状态.
逃离IO单子是不可能的.[嗯,不,你可以使用unsafePerformIO.不要这样做!]因此,程序将执行的所有I/O都捆绑到一个巨大的IO块中,从而强制执行操作的全局排序.[至少,直到你打电话forkIO,但无论如何...]
原因unsafePerformIO是如此不安全的是,没有办法弄清楚封闭的I/O操作何时,是否或多少次 - 这通常是一件非常糟糕的事情.
该ST单子还提供可变的状态,但它确实有一个逃生机制- runST功能.这使您可以将不纯的值转换为纯值.但现在无法保证单独ST块的运行顺序.为了防止完全破坏,我们需要确保单独的ST块不能相互"干扰".
因此,您无法在STmonad中执行任何I/O操作.您可以访问可变状态,但不允许该状态转义ST块.
该IO单子和ST单子实际上是相同的单子.并且IORef实际上是一个STRef,依此类推.因此,能够编写代码并在两个monad中使用它真的非常有用.你提到的所有四个函数都是类型转换,可以让你做到这一点.
要了解危险,我们需要了解如何ST实现它的小技巧.它都是s类型签名中的幻像类型.要运行一个ST块,它需要为所有可能的方法工作s:
runST :: (forall s. ST s x) -> x
Run Code Online (Sandbox Code Playgroud)
所有可变的东西都有s类型,并且由于一个快乐的意外,这意味着任何将可变东西从STmonad中返回的尝试都是错误的.(这真的是一个黑客,但它的工作非常完美...)
至少,如果你使用它将是错误的类型runST.请注意,ioToST给你一个ST RealWorld x.粗略地说,IO x≈ ST RealWorld x.但runST不会接受这一点作为输入.所以你不能runST用来运行I/O.
该ioToST给你,你可以不使用类型runST.但是unsafeIOToST给你一个可以正常工作的类型runST.那时,你基本上已经实现了unsafePerformIO:
unsafePerformIO = runST . ioToST
Run Code Online (Sandbox Code Playgroud)
将unsafeSTToIO让你获得可变的东西出来一个ST块,并有可能进入另一个:
foobar = do
v <- unsafeSTToIO (newSTRef 42)
let w = runST (readSTRef v)
let x = runST (writeSTRef v 99)
print w
Run Code Online (Sandbox Code Playgroud)
想猜一下打印什么?因为事情是,我们在ST这里有三个动作,它们可以完全按任何顺序发生.会readSTRef发生在之前还是之后writeSTRef?
[实际上,在这个例子中,写入永远不会发生,因为我们不会"做"任何事情x.但是如果我传递x给代码中一些遥远的,无关的部分,并且代码碰巧检查它,那么突然我们的I/O操作会做出不同的事情.纯代码不应该影响那样的可变东西!]
编辑:看来我有点不成熟.该unsafeSTToIO功能允许您利用一个可变值出的ST单子,但它似乎它需要第二个电话来unsafeSTToIO把可变回事情到了ST单子一次.(此时,两个动作都是IO动作,因此保证了它们的顺序.)
你当然可以混合一些unsafeIOToST,但这并不能证明它unsafeSTToIO本身是不安全的:
foobar = do
v <- unsafeSTToIO (newSTRef 42)
let w = runST (unsafeIOToST $ unsafeSTToIO $ readSTRef v)
let x = runST (unsafeIOToST $ unsafeSTToIO $ writeSTRef v 99)
print w
Run Code Online (Sandbox Code Playgroud)
我打得四处这一点,我还没有成功地说服类型检查,让我用做一些可证明不安全只 unsafeSTToIO.我仍然相信它可以做到,关于这个问题的各种评论似乎都同意,但我实际上无法构建一个例子.你明白了; 改变类型,你的安全被打破.
| 归档时间: |
|
| 查看次数: |
438 次 |
| 最近记录: |