Haskell and Monads type signatures
I made a function in haskell that should take a list along with the size of the list; and it is supposed to create a Data.Vector.Mutable.MVector with the given size, fill the vector with the contents of the list and return its vector.
TL; DR
- I want to know why the type signature I put in was not working. What am I missing about this that makes it unacceptable as a type signature?
- Is it possible to create a function that does what I indicated above when using my type signature?
- How do I interpret the compiler generated type signature based on the code I wrote?
This is the function:
vecFromList lst sz = MV.new sz >>= (\vec -> fillV (zip [0..sz - 1] lst) vec) where
fillV [] vec = vec
fillV ((i, v):xs) vec = MV.write vec i v >> fillV xs vec
I wrote most of them without trying to figure out what they do (last line), and as a consequence, I cannot think of a suitable type signature. However, the compiler came out to save the day:
compiler generated type signature
vecFromList
:: (PrimMonad (MVector t), PrimState (MVector t) ~ t) =>
[b] -> Int -> MVector t b
I heard someone said Wat? Oh, it was just me, anyway ... Before I tried to compile it, this is the type signature I thought should work:
The one I thought should work
vecFromList :: PrimMonad m => [t] -> Int -> MV.MVector (PrimState m) t
It should be obvious at this point that this somewhat simplified prominent type signature, which seems to be similar to what I want the function to do, does n't really work . To come up with a type signature, I used the type signatures of some of the other functions in the vector module that I thought were similar to it, for example:
Data.Vector.Mutable.read :: PrimMonad m => MVector (PrimState m) a -> Int -> m a
Now, I'm still relatively new to haskell, so I'm still trying to get used to the symbols and signs used in this language, and especially close to understanding why what seems like a simple task should get so confusing about Monands. For example, what is the goal MVector
looking like this MVector :: * -> * -> *:
??
source to share
You're almost there. You would expect the type signature to be correct, except that the resultant MVector
must be in a monad m
:
vecFromList :: PrimMonad m => [t] -> Int -> m (MV.MVector (PrimState m) t)
The function fillV
must be of type
fillV :: [(Int, t)]
-> MV.MVector (PrimState m) t -> m (MV.MVector (PrimState m) t)
but you []
case cast a vector without return
to a type m
. Here's a working version:
vecFromList :: PrimMonad m => [t] -> Int -> m (MV.MVector (PrimState m) t)
vecFromList lst sz = MV.new sz >>= (\vec -> fillV (zip [0..sz - 1] lst) vec) where
fillV [] vec = return vec
fillV ((i, v):xs) vec = MV.write vec i v >> fillV xs vec
and a working example:
> V.create $ vecFromList [1,2,3] 3
fromList [1,2,3]
Note that you are not actually modifying vec
in your function fillV
, you are only referring to it, you can use the function for_
from Data.Foldable
instead of explicitly writing the loop. I usually write mutable vector code in blocks do
because it makes things clearer to me:
vecFromList2 :: PrimMonad m => [t] -> Int -> m (MV.MVector (PrimState m) t)
vecFromList2 l n = do
v <- MV.new n
for_ (zip [0..n - 1] l) $ \(i,a) -> MV.write v i a
return v
Unfortunately, working with mutable vectors in Haskell can be difficult and requires practice. Using TypedHoles and PartialTypeSignatures might help.
The reason MVector
is PrimState m
, so it can work with ST
or IO
. You can find an explanation for this here .
source to share