7

我想要类似的东西

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 评论的这个问题相同的位置:

[I]如果您想在某个结构中保留多态函数,您需要专门的数据类型(例如 newtype I = I (forall a.a -> a))或 ImpredicativeTypes。

另外,请参阅这个问题。问题是,这些都是非常丑陋的解决方案。所以我提出了第三种选择,即通过运行“应该”作为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,但如果我的替代方案是包装或禁止类型......显然,我并不孤单。

有什么理由我不应该在这里使用unsafePerformIO吗?在这种情况下,真的一点都不安全吗?是否有性能考虑或其他我应该注意的事项?

- - - - - - - 编辑 - - - - - - - -

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

4

1 回答 1

5

我不能说我完全理解问题陈述,但以下文件在 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

现在让我们解决STvs的IO问题。它被调用unsafePerformIO而不是被调用的原因unusablePerformIO是因为它带有一个编译器无法检查的证明负担:你正在运行的东西unsafePerformIO必须表现得好像它是引用透明的。由于ST动作带有一个(经过编译器检查的)证明,证明它们在使用 执行时是透明的runST,这意味着在会进行类型检查unsafePerformIO的代码上使用不会ST比在使用中更危险runST

但是:从软件工程的角度来看存在危险。由于证明不再经过编译器检查,因此将来的重构更容易违反可以安全使用的条件unsafePerformIO。因此,如果可以避免它(因为它似乎在这里),你应该努力这样做。(此外,“不再有危险”并不意味着“没有危险”:unsafeFreeze您正在拨打的电话有自己必须满足的证明责任;但是您已经必须满足该证明责任才能使ST代码是正确的。)

于 2013-11-15T03:41:43.570 回答