我想要类似的东西

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 ----------------

下面的答案向我展示了如何完全解决此问题,这很好。但是我仍然对出于教育目的的原始问题(使用可变向量时runSTunsafePerformIO的含意)感兴趣。

最佳答案

我不能说我完全理解问题说明,但是下面的文件在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/

10-10 18:13