Haskell: Failed to get error using runST

When I try to compile this:

module Main where import qualified Data.Vector.Unboxed.Mutable as MV import Control.Monad.ST myRead mv = runST $ MV.read mv 0 

The following error message appears:

 Could not deduce (t ~ U.MVector sa) from the context (U.Unbox a) bound by the inferred type of myRead :: U.Unbox a => t -> a at src/Main.hs:53:1-32 `t' is a rigid type variable bound by the inferred type of myRead :: U.Unbox a => t -> a at src/Main.hs:53:1 Expected type: U.MVector (PrimState (ST s)) a Actual type: t In the first argument of `MV.read', namely `mv' In the second argument of `($)', namely `MV.read mv 0' In the expression: runST $ MV.read mv 0 

Can I make reading from a mutable vector clean with runST? If so, how? I assume this entails a type signature for myRead , but everything I tried just led to more and more obscure error messages appearing.

EDIT : highlighting some context, I just added a comment below: The context here is that I have a function that accepts a mutable vector, performs some calculations using the mutable vector as a temporary scratch space, then you need to return the float value. Since I do not need changes in the mutable vector, I was wondering if there was a way to ignore its “state change” and simply return one of the values ​​from the inside.

+7
source share
3 answers

The other answers are good, but I think there is one basic thing about ST that you are missing. Each runST call effectively creates a new "ST universe" in which some imperative code is executed. Therefore, if you have one call to runST to make an array and a separate call to startST to get the value from this array, everything cannot work. Two runST calls want to have their own unique universes, while you want them to share them.

What answers are explained in detail, how these unique universes are created using some type system trick.

+2
source

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.

 {-#LANGUAGE Rank2Types#-} module Main where import qualified Data.Vector.Unboxed.Mutable as MV import Control.Monad.ST myRead :: MV.Unbox a => (forall s . MV.MVector sa) -> a myRead mv = runST $ MV.read mv 0 

The future signature of your function will be like this if I understand:

 -- myBadRead :: forall sa . MV.Unbox a => MV.MVector sa -> a -- myBadRead mv = runST $ MV.read mv 0 

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

+3
source

The applicative answer will tell you how to get the code to compile. But the code will not be used: the runST point is that imperative computation cannot avoid this due to the existing type variable.

Now, any modified array created somewhere will be of type MVector sa for a fixed s, and your myRead expects a value that provides a vector for any s.

It seems that there used to be a problem because of which you wanted to have this (impossible) function.

+3
source

All Articles