Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: "Could not deduce" error with runST

Tags:

haskell

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.

like image 938
brooks94 Avatar asked Aug 03 '12 19:08

brooks94


2 Answers

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.

like image 58
Joachim Breitner Avatar answered Sep 25 '22 10:09

Joachim Breitner


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

like image 23
applicative Avatar answered Sep 25 '22 10:09

applicative