There are two problems. First, as dave4420 pointed out, runST
needs the alter
function to be polymorphic in the state s
.
Fixing that, however, makes it impossible to solve the second problem, that you need an MArray
instance for thaw
(and freeze
). You would need a constraint
alterUArray :: (Ix i, Ord e, IArray UArray e, forall s. MArray (STUArray s) e (ST s)) => ...
to get it to work, since runST
is the one to choose s
. But you can't specify such a constraint.
If you give a specific element type (Int
, Double
, ...), it works since there is an
instance MArray (STUArray s) Int (ST s) where ...
so the demands of thaw
and freeze
are satisfied no matter what s
is chosen by runST
(and the constraint need not be stated).
It also works if you choose boxed arrays instead of unboxed ones, since there also is an
instance MArray (STArray s) e (ST s) where ...
and so there is no constraint on the element type that needs to be stated in the signature of alterUArray
. (There is no constraint on the types of list elements, and list elements are boxed, so boxed arrays are the correspondence to lists, not unboxed arrays).
If you can bear getting your hands dirty, you can circumvent the problem by replacing ST s
by 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
only needs FlexibleContexts
. This allows to pass a bad alter
argument that does nefarious IO
stuff, though, and would hide it from the caller. So let us make the use of unsafePerformIO
here safe, by forcing a more general type on the alter
argument:
{-# 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
Now we have given alter
a type that makes it impossible to do nefarious IO
without itself using unsafePerformIO
, so the use of unsafePerformIO
here doesn't introduce additional insecurity - at the expense of more needed extensions.
(Note: while using thaw
to get a copy of the original array is necessary, there is no need for an additional copy when freezing, that could be unsafeFreeze
without a problem.)