How do I thaw, mutate, then refreeze a Haskell Vector?

235 views Asked by At

I'm working through a tutorial on Haskell Vectors that, as an exercise, asks you to reimplement Data.Vector.Unboxed.modify using runST and "functions introduced earlier in the tutorial" which I take to be thaw & freeze.

This is what I've tried:

#!/usr/bin/env stack
-- stack --resolver lts-12.21 script
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Unboxed.Mutable as VM
-- import Data.Vector.Unboxed (modify)
import Control.Monad.ST

-- TODO: debug this...
modify :: VM.Unbox a => (VM.MVector s a -> ST s ()) -> V.Vector a -> V.Vector a
modify sa v = runST $ do
  mv <- V.thaw v
  sa mv
  V.freeze mv

main :: IO ()
main = do
  let vec = V.enumFromTo 1 10 :: V.Vector Int
  print $ modify (\v -> fmap (\_-> ()) $ VM.nextPermutation v) vec

Which gets compile error:

$ ./modify.hs
./modify.hs:11:9: error:                                                                                            
    • Couldn't match type ‘s1’ with ‘s’                                                                                                                        
      ‘s1’ is a rigid type variable bound by                                                                                                                   
        a type expected by the context:                                                                                                                        
          forall s1. ST s1 (V.Vector a)                                                                                                                        
        at /home/tom/code/fpco-haskell-tutorials/vector/modify.hs:(10,15)-(13,13)                                                                              
      ‘s’ is a rigid type variable bound by                                                                                                                    
        the type signature for:                                                                                                                                
          modify :: forall a s.                                                                                                                                
                    VM.Unbox a =>                                                                                                                              
                    (VM.MVector s a -> ST s ()) -> V.Vector a -> V.Vector a                                                                                    
        at /home/tom/code/fpco-haskell-tutorials/vector/modify.hs:9:1-79
      Expected type: ST s1 (VM.MVector s a)                                               
        Actual type: ST                        
                       s1                    
                       (VM.MVector
                          (primitive-0.6.3.0:Control.Monad.Primitive.PrimState (ST s1)) a)
    • In a stmt of a 'do' block: mv <- V.thaw v
      In the second argument of ‘($)’, namely
        ‘do mv <- V.thaw v
            _ <- sa mv       
            V.freeze mv’ 
      In the expression:  
        runST                  
          $ do mv <- V.thaw v          
               _ <- sa mv                                                       
               V.freeze mv                                               
    • Relevant bindings include                                                 
        sa :: VM.MVector s a -> ST s ()
          (bound at /home/tom/code/fpco-haskell-tutorials/vector/modify.hs:10:8)
        modify :: (VM.MVector s a -> ST s ()) -> V.Vector a -> V.Vector a
          (bound at /home/tom/code/fpco-haskell-tutorials/vector/modify.hs:10:1)
   |
11 |   mv <- V.thaw v                                              
   | 

I think the important bit is:

      Expected type: ST s1 (VM.MVector s a)                                               
        Actual type: ST                        
                       s1                    
                       (VM.MVector
                          (primitive-0.6.3.0:Control.Monad.Primitive.PrimState (ST s1)) a)

Looking at my attempt at modify:

modify :: VM.Unbox a => (VM.MVector s a -> ST s ()) -> V.Vector a -> V.Vector a
modify sa v = runST $ do
  mv <- V.thaw v
  sa mv
  V.freeze mv

What I'm trying to express is:

  • Thaw the Vector into a MVector
  • Perform the mutation
  • Freeze into a new Vector

If I comment out where I try to perform the mutation:

modify sa v = runST $ do
  mv <- V.thaw v
  -- sa mv
  V.freeze mv

the code compiles and runs to produce an unmodified vector.

$ ./modify.hs 
[1,2,3,4,5,6,7,8,9,10]

So there's something wrong with the way I'm trying to apply the stateful action sa to modify the mutable vector mv.

Looking at the types, and the docs for thaw and freeze:

thaw :: (Unbox a, PrimMonad m) => Vector a -> m (MVector (PrimState m) a) 

freeze :: (Unbox a, PrimMonad m) => MVector (PrimState m) a -> m (Vector a)

sa mv :: ST s ()

So the do block is inside a monad m in typeclass PrimMonad.

My thought was that the ST s would "line up" with that to perform the mutation...

I tried binding that to a hole:

modify sa v = runST $ do
  mv <- V.thaw v
  _ <- sa mv
  V.freeze mv

but that made no difference, which thinking about it, you wouldn't expect it to...

I think this is a general question about monad transformers. Is there some lifting I need to do here?

What so I need to do to perform the mutation of the mutable vector with my sa?

1

There are 1 answers

1
Daniel Wagner On

Just change the type signature:

modify :: VM.Unbox a => (forall s. VM.MVector s a -> ST s ()) -> V.Vector a -> V.Vector a
--                       ^^^^^^^^^

Your initial implementation was fine. But, you should use one of the existing modify functions instead of rolling your own.