By default, the compiler sees the mv argument on the left as a specific type, but requires the right to a polymorphic type on the right. A type signature can fix the situation.
{-
The future signature of your function will be like this if I understand:
but here runST :: (forall s. ST sa) -> a will not have an s independent thing to work on, since s is fixed when writing mv in LHS.
Edit: However, as Joachim B. and Daniel F. emphasize, although the above definition is coherent, in practice this will be useless since you cannot construct the vector mv to give it. To put it bluntly, any way of generating mv will already have the demineral s assigned to it under the hood by the compiler. One standard method is to force such a function to act on a “clean” vector from Data.Vector.Unboxed , then thaw it on the right side before applying operations from the .Mutable module
import qualified Data.Vector.Unboxed as UV import qualified Data.Vector.Unboxed.Mutable as MV import Control.Monad.ST myRead :: MV.Unbox a => UV.Vector a -> a myRead v = runST $ do mv <- UV.unsafeThaw v MV.read mv 0
Of course, this particular definition is equivalent to myRead = (UV.! 0) Similarly, something like this makes sense, hitting runST in the definition of myRead
mhead :: MV.Unbox a => MV.MVector sa -> ST sa mhead mv0 = MV.read mv0 0 mrx = runST $ do mv <- UV.unsafeThaw $ UV.enumFromStepN 0 1 20 -- ^^^ or however the mv is generated. x <- MV.unsafeRead mv 17 -- arbitrary 'scratch pad' MV.unsafeWrite mv 17 (2*x) -- computations mhead mv -- ^^^ we return just the first element, after all the mutation
Here, instead of closing myRead or mhead with runST , we keep it polymorphic in s and then we can use it inside the same ST block in which the mutable vector mv appears. Thus, the compiler will be able to use the "secret" s , which it uses for the do block as a whole, to interpret the result of applying mhead to mv , as this is one of the possibilities left open by our polymorphic definition of mhead
applicative
source share