有两个问题。首先,如dave4420 pointed out,runST
需要alter
函数在状态s
中是多态的。
然而,解决这个问题使得解决第二个问题成为不可能,您需要(和freeze
)的MArray
实例。您将需要一个约束
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
参数,虽然这个参数确实会造成一些不必要的损失,但它会将其从调用者中隐藏起来。因此,让我们把使用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
没有问题。)
如果删除类型标注从'解冻ua',你会得到什么错误? – dave4420
@ dave4420 http://hpaste.org/84910 –