我想要类似的东西
f :: [forall m. (Mutable v) (PrimState m) r -> m ()] -> v r -> v r -- illegal signature
f gs x = runST $ do
y <- thaw x
foldM_ (\_ g -> g y) undefined gs -- you get the idea
unsafeFreeze y
我基本上处于Vitus评论的this question的相同位置:
另外,请参见this question。问题是,这些都是非常丑陋的解决方案。因此,我提出了第三种选择,即通过在
ST
中运行“应该”是IO
计算来完全避免多态性。因此,f
变为:f :: [(Mutable v) RealWorld r -> IO ()] -> v r -> v r
f gs x = unsafePerformIO $ do
y <- thaw x
foldM_ (\_ g -> g y) undefined gs -- you get the idea
unsafeFreeze y
与“安全”的
unsafe
路由相比,我觉得使用IO
ST
路由有点脏,但是如果我的选择是包装器或强制性类型...显然,I'm not alone.有什么原因我不应该在这里使用
unsafePerformIO
吗?在这种情况下,真的不安全吗?是否有性能方面的考虑或我应注意的其他事项?-------------- EDIT ----------------
下面的答案向我展示了如何完全解决此问题,这很好。但是我仍然对出于教育目的的原始问题(使用可变向量时
runST
与unsafePerformIO
的含意)感兴趣。 最佳答案
我不能说我完全理解问题说明,但是下面的文件在GHC 7.6.2下编译时没有错误。它与第一个示例具有相同的主体(尤其是根本不调用unsafePerformIO
);主要区别在于forall
已移出所有类型构造函数。
{-# LANGUAGE RankNTypes #-}
import Control.Monad
import Control.Monad.Primitive (PrimState)
import Control.Monad.ST
import Data.Vector.Generic hiding (foldM_)
f :: Vector v r => (forall m. [Mutable v (PrimState m) r -> m ()]) -> v r -> v r
f gs x = runST $ do
y <- thaw x
foldM_ (\_ g -> g y) undefined gs
unsafeFreeze y
现在,让我们解决
ST
vs IO
问题。之所以称为unsafePerformIO
而不是unusablePerformIO
,是因为它带有编译器无法检查的证明负担:您正在运行unsafePerformIO
的事物必须表现得好像是参照透明的。由于ST
操作带有(经过编译器检查的)证明,它们在与runST
执行时具有透明的行为,因此,这意味着在对unsafePerformIO
进行类型检查的代码上使用ST
不会比使用runST
危险更大。但:从软件工程的角度来看存在危险。由于不再对编译器进行检查,因此,将来的重构更容易违反可以安全使用
unsafePerformIO
的条件。因此,如果有可能避免出现这种情况(如此处所示),则应努力做到这一点。 (此外,“没有更多的危险”并不表示“没有危险”:您正在进行的unsafeFreeze
调用有它自己必须满足的证明负担;但是那时您已经必须满足ST
的证明负担。代码正确无误。)关于haskell - runST与unsafePerformIO的实际含义,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19982295/