我在haskell中创建了一个函数,它应该列出列表的大小;它应该创建一个具有给定大小的Data.Vector.Mutable.MVector,用向量的内容填充向量并返回该向量。
TL; DR
这是功能:
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
我写的大部分内容都没有真正理解他们做了什么(最后一行),因此,我无法想出合适的类型签名。但是,编译器介入以保存这一天:
编译器生成的类型签名
vecFromList
:: (PrimMonad (MVector t), PrimState (MVector t) ~ t) =>
[b] -> Int -> MVector t b
我听到有人说 Wat?哦,那只是我,反正...... 在我尝试编译它之前,这是我认为应该工作的类型签名:
我认为应该工作的那个
vecFromList :: PrimMonad m => [t] -> Int -> MV.MVector (PrimState m) t
这一点应该是显而易见的,这种看似简单的外观类型签名看起来与我想要的功能完全一样,实际上不工作。为了提出类型签名,我使用了矢量模块中我认为与它类似的一些其他函数的类型签名,例如这个:
Data.Vector.Mutable.read
:: PrimMonad m => MVector (PrimState m) a -> Int -> m a
现在,我仍然相对较新的haskell,所以我仍然习惯于习惯语言中使用的符号和符号,特别是接近理解为什么看起来简单的任务必须变得如此复杂的事情到期到Monands。例如,MVector
具有此类MVector :: * -> * -> *:
??
答案 0 :(得分:5)
你几乎就在那里。您预期的类型签名是正确的,除非生成的MVector
需要在monad m
中:
vecFromList :: PrimMonad m => [t] -> Int -> m (MV.MVector (PrimState m) t)
fillV
函数的类型应为
fillV :: [(Int, t)]
-> MV.MVector (PrimState m) t -> m (MV.MVector (PrimState m) t)
但是你[]
案例会在没有return
m
类型的情况下提供向量。这是一个有效的版本:
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
和一个工作示例:
> V.create $ vecFromList [1,2,3] 3
fromList [1,2,3]
请注意,您实际上并未修改vec
函数中的fillV
,只引用它,您可以使用for_
中的Data.Foldable
函数代替明确地写一个循环。我通常在do
块中编写可变矢量代码,因为它使我更清楚:
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
不幸的是,在Haskell中使用可变向量会变得棘手,需要练习。使用TypedHoles和PartialTypeSignatures可以提供帮助。
原因MVector
包含PrimState m
,因此它可以与ST
或IO
一起使用。你可以找到它的解释here。