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 aMVector
- 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
?
Just change the type signature:
Your initial implementation was fine. But, you should use one of the existing
modify
functions instead of rolling your own.