I want something like
f :: [forall m. (Mutable v) (PrimState m) r -> m ()] -> v r -> v r -- illegal signature
f gs x = runST $ do
y <- thaw x
foldM_ (\_ g -> g y) undefined gs -- you get the idea
unsafeFreeze y
I'm essentially in the same position I was in this question where Vitus commented:
[I]f you want keep polymorphic functions inside some structure, you need either specialized data type (e.g. newtype I = I (forall a. a -> a)) or ImpredicativeTypes.
Also, see this question. The problem is, these are both really ugly solutions. So I've come up with a third alternative, which is to avoid the polymorphism altogether by running what "should" be a ST
computation in IO
instead. Thus f
becomes:
f :: [(Mutable v) RealWorld r -> IO ()] -> v r -> v r
f gs x = unsafePerformIO $ do
y <- thaw x
foldM_ (\_ g -> g y) undefined gs -- you get the idea
unsafeFreeze y
I feel slightly dirty for going the unsafe
IO
route compared the to the "safe" ST
route, but if my alternative is a wrapper or impredicative types... Apparently, I'm not alone.
Is there any reason I shouldn't use unsafePerformIO
here? In this case, is it really unsafe at all? Are there performance considerations or anything else I should be aware of?
--------------EDIT----------------
An answer below shows me how to get around this problem altogether, which is great. But I'm still interested in the original question (implicaitons of runST
vs unsafePerformIO
when using mutable vectors) for educational purposes.