Here's a quick example of how to use ST arrays
import Data.Array.ST hiding (unsafeThaw) -- Deprecated
import Data.Array (Array)
import Data.Array.Unsafe (unsafeThaw) -- If you really really really need it
newtype W a = W {arr :: Array Integer a}
modifyW :: a -> W a -> W a
modifyW v (W arr) = W $ runSTArray $ do -- The double $ is on purpose, . doesn't
-- play so well with Rank n types.
a <- thaw arr -- Turn `arr` into something we can modify
writeArray a 1 v -- modify it
return a
This will ensure the computation is pure, but the array will not be copied inside modifyW, all those modifications are constant time. If you can't afford any copying at all you can use unsafeThaw. But this is well.. unsafe. It'll actually modify the pure array so you have to be extremely careful not to try to use the pure structure after modifyW is run. This is much harder than it sounds thanks to lazy evaluation so I'd caution you against this.
With this style, you read from the pure Array, then when you need to modify it, you run in the ST monad which let's you do impure things, but won't let them bleed into the rest of your program.
STmonad isn't better suited for your needsSTArray, and note thatrunSTcan turn anSTaction into a pure value.data W s = W{ arr :: STArray s Int Node, ...}. Do I just have to create the array entirely before putting it in the wrapper? That could be a viable way of doing it