7
votes

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

I get the following error message:

Could not deduce (t ~ U.MVector s a)
    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 a read from a mutable vector pure with runST? If so, how? I assume it entails a type signature for myRead, but everything I've tried has just lead to more and more incomprehensible error messages.

EDIT: Highlighting some context I just put in a comment below: The context here is that I have a function that takes in a mutable vector, does some computations using the mutable vector as a temporary scratch space, then needs to return a float value. Because I don't care about the changes to the mutable vector, I was wondering if there was a way to ignore its "state change" and simply return one of the values from inside it.

3
I think the opinion of the wise, viz Breitner and Fischer, is that we need to know more about how you got to this point. What function is producing the mutable vector you propose to apply myRead to? See the last sentence in Breitner's response. If we knew what was putting you in this corner, we could say how to re-analyze things so they hang together. A few more lines of code would thus be helpful. For example, if mv is inside an ST block, the simpler definition myRead mv = MV.read mv 0 (no runST) could be used.applicative
Let me reinforce what applicative said: We need more context. Without knowing what you actually want to achieve, we can't find the proper solution.Daniel Fischer

3 Answers

2
votes

The other answers are good, but I think there is one basic thing about ST that you are missing. Each call to runST is effectively making a new "ST universe" in which some imperative code runs. So if you have one call to runST to make the array and a separate call to runST to get a value out of that array, things can't possibly work. The two runST calls want their own unique universes, whereas you want them to share one.

What the answers explained in some detail is how these unique universes are created by some type-system trickery.

3
votes

The answer by applicative tells you how to get your code to compile. But the code will not be usable: The point of runST is that the imperative computation cannot escape it, due to the existentially bound type variable there.

Now any mutable array that you create somewhere will have type MVector s a for a fixed s, while your myRead expects a value that provides a vector for any s.

It seems that there is a problem earlier that made you want to have that (impossible) function.

3
votes

The compiler defaults to seeing the argument mv on the the left hand side as of some specific type, but requires something of polymorphic type on the right. A type signature can fix things.

{-#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 s a) ->  a
myRead mv =  runST $ MV.read mv 0

The would-be signature of your function would be something like this, if I understand:

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

but here runST :: (forall s. ST s a) -> a wouldn't have an s-independent thing to work on, since s is fixed when you write mv on the LHS.

Edit: However, as Joachim B and Daniel F. emphasize, though the above definition is coherent, it will be of no use in practice, since you will not be able to construct a vector mv to give to it. To put it crudely, any way of generating an mv will already have a deteminate s assigned to it under the hood by the compiler. One standard method is to make such a function act on a 'pure' vector from Data.Vector.Unboxed then, on the right hand side, thaw it 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 would make sense, striking the runST in the definition of myRead

mhead :: MV.Unbox a => MV.MVector s a -> ST s a
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, rather than closing off myRead or mhead with runST we keep it polymorphic in s and can then use it inside the same ST block in which the mutable vector mv appears. The compiler will thus be able to use the 'secret' s it is using for the do-block as a whole to interpret the result of applying mhead to mv, since it is one of the possibilities left open by our polymorphic definition of mhead