有两个问题。首先,正如dave4420 所指出的,状态中runST
需要alter
函数是多态的s
。
然而,解决这个问题就无法解决第二个问题,即您需要(and )的MArray
实例。你需要一个约束thaw
freeze
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
和的要求(并且不需要说明约束)。freeze
s
runST
如果您选择盒装数组而不是未盒装数组,它也可以工作,因为还有一个
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
没有问题。)