Even simpler, Freezer could just be a Monad denoting arbitrary "cleanup" actions, as long as they don't involve writes.
-- | A computation that is guaranteed to run after all modifications are complete.
newtype Freeze s a = {- private constructor and accessor -} Freeze { getFreeze :: ST s a } deriving (Functor, Applicative, Monad)
freezeArray :: (Ix i, MArray a e (ST s), IArray b e) => a i e -> Freeze s (b i e)
freezeArray arr = Freeze (unsafeFreeze arr)
-- Reads are fine too, just not writes
freezeRef :: STRef s a -> Freeze s a
freezeRef ref = Freeze (readSTRef ref)
runSTFreeze :: (forall s . ST s (Freeze s a)) -> a
runSTFreeze act = runST (act >>= getFreeze)
The proposed -Trav and -With functions can be implemented straightforwardly from that if desired:
runSTArrayTrav m = runSTFreeze (m >>= traverse freezeArray)
runSTUArrayTrav m = runSTFreeze (m >>= traverse freezeArray)
runSTArrayWith tr m = runSTFreeze (m >>= tr freezeArray freezeArray)