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 :: * -> * -> *:

??

+3


source to share


1 answer


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 .

+5


source







All Articles