我想要类似的东西
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
我基本上处于与 this question 相同的位置Vitus 评论道:
[I]f you want keep polymorphic functions inside some structure, you need either specialized data type (e.g. newtype I = I (forall a. a -> a)) or ImpredicativeTypes.
另见 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
这里?在这种情况下,真的一点都不安全吗?是否有性能考虑或其他我应该注意的事项?--------------编辑----
下面的答案向我展示了如何完全解决这个问题,这很棒。但我仍然对出于教育目的的原始问题(
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
对比 IO
问题。它被称为 unsafePerformIO
的原因而不是 unusablePerformIO
是因为它带有编译器无法检查的证明负担:您正在运行的东西unsafePerformIO
on 必须表现得好像它是引用透明的。由于ST
Action 带有(经过编译器检查的)证明,证明它们在使用 runST
执行时行为透明。 ,这意味着使用 unsafePerformIO
没有更多危险关于将在 ST
中进行类型检查的代码比使用 runST
.但是 : 从软件工程的角度来看存在危险。由于证明不再经过编译器检查,因此将来的重构更容易违反可以安全使用
unsafePerformIO
的条件。 .因此,如果可以避免它(因为它似乎在这里),你应该努力这样做。 (此外,“不再有危险”并不意味着“没有危险”:您正在调用的 unsafeFreeze
有其自己必须满足的举证责任;但是您已经必须满足该举证责任ST
代码是正确的。)
关于haskell - runST 与 unsafePerformIO 的实际意义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19982295/