有两个问题。首先,正如dave4420 所指出的,状态中runST需要alter函数是多态的s。
然而,解决这个问题就无法解决第二个问题,即您需要(and )的MArray实例。你需要一个约束thawfreeze
alterUArray :: (Ix i, Ord e, IArray UArray e, forall s. MArray (STUArray s) e (ST s)) => ...
让它工作,因为runST是选择的一个s。但是您不能指定这样的约束。
如果你给出一个特定的元素类型(Int, Double, ...),它可以工作,因为有一个
instance MArray (STUArray s) Int (ST s) where ...
因此,无论选择什么,都可以满足thaw和的要求(并且不需要说明约束)。freezesrunST
如果您选择盒装数组而不是未盒装数组,它也可以工作,因为还有一个
instance MArray (STArray s) e (ST s) where ...
因此对需要在alterUArray. (对列表元素的类型没有限制,并且列表元素是装箱的,所以装箱的数组是列表的对应关系,而不是未装箱的数组)。
如果你能忍受弄脏你的手,你可以通过替换来规避这个ST s问题IO,
alterUArray :: (Ix i, Ord e, MArray IOUArray e IO, IArray UArray e) =>
(IOUArray i e -> IO ()) -> UArray i e -> UArray i e
alterUArray alter ua = unsafePerformIO $ do
mua <- thaw ua
alter mua
freeze mua
只需要FlexibleContexts。但是,这允许传递一个错误的alter参数,该参数会做一些邪恶的IO事情,并且会对调用者隐藏它。因此,让我们unsafePerformIO通过在参数上强制使用更通用的类型来安全地使用这里alter:
{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables #-}
import Data.Array.Unboxed
import Data.Array.IO
import System.IO.Unsafe
alterUArray :: forall i e. (Ix i, Ord e, IArray UArray e, MArray IOUArray e IO) =>
(forall m u. MArray u e m => u i e -> m ()) -> UArray i e -> UArray i e
alterUArray alter ua = unsafePerformIO $ do
mua <- thaw ua :: IO (IOUArray i e)
alter mua
freeze mua
现在我们已经给出alter了一个类型,它使得IO不使用自身就无法进行恶意操作unsafePerformIO,所以unsafePerformIO这里的使用不会引入额外的不安全性——以更多需要的扩展为代价。
(注意:虽然thaw需要获取原始数组的副本,但冻结时不需要额外的副本,这可能unsafeFreeze没有问题。)