简体   繁体   中英

Haskell type signatures and Monads

I made a function in haskell that is supposed to 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 this vector.

TL;DR

  • I want to know why the type signature I supplied didn't work. What am I missing from this that makes it unacceptable as a type signature?
  • Is it possible to create a function that does what I specified above while using my type signature?
  • How does one interpret the type signature generated by the compiler 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

Most of it I wrote without really trying to understand what they do (the last line) and as a consequence, I am not able to come up with a suitable type signature. However, the compiler stepped in to save the day with this:

Compiler generated type signature

vecFromList
  :: (PrimMonad (MVector t), PrimState (MVector t) ~ t) =>
     [b] -> Int -> MVector t b

Did I hear someone say Wat? Oh that was just me, anyways... Before I tried compiling 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 by this point that this somewhat simplistic looking type signature which seems to look exactly like what I want the function to do, does in fact not work. To come up with the type signature, I was using the type signatures of some of the other functions in the vector module that I thought were similar to it, like this one 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 am still trying to get used to the symbols and signs used in the language and especially come close to understanding why what appears to be simple tasks have to become such convoluted things due to Monands. For example, what is the purpose of MVector having this kind MVector :: * -> * -> *: ??

You're almost there. You're expected type signature is correct except the resulting MVector needs to be in the monad m :

vecFromList :: PrimMonad m => [t] -> Int -> m (MV.MVector (PrimState m) t)

The fillV function should have type

fillV :: [(Int, t)]
             -> MV.MVector (PrimState m) t -> m (MV.MVector (PrimState m) t)

but you're [] case gives the vector without return ing it to the m type. 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]

Notice that you don't actually modify vec in your fillV function, you only reference it, you could use the for_ function from Data.Foldable instead of explicitly writing a loop. I normally write mutable vector code in do blocks because it makes things clearer for 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 get tricky and it takes practice. Using TypedHoles and PartialTypeSignatures can help.

The Reason MVector has the PrimState m it so it can work with ST or IO . You can find an explanation of it here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM