Haskell类型签名和Monad

4
我在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还比较陌生,仍在努力适应语言中使用的符号和标志,特别是试图理解为什么看起来很简单的任务由于Monads而变得如此复杂。例如,MVector具有这种类型MVector :: * -> * -> *:的目的是什么?

1个回答

5
你已经接近成功了。你期望的类型签名是正确的,只是产生的MVector需要在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)

但是你的[]案例提供了向量,但没有将其returnm类型中。这里是一个可行的版本:

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]

请注意,在您的fillV函数中实际上没有修改vec,您只是引用它,您可以使用Data.Foldable中的for_函数而不是显式编写循环。我通常在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中使用可变向量可能会变得棘手,需要练习。使用TypedHolesPartialTypeSignatures可以帮助解决问题。

MVectorPrimState m是为了能够与STIO一起使用。您可以在此处找到其说明。


你是否想写 forM_ 而不是 for_ ?谢谢。 - smac89
@Smac89 for_forM_ 本质上是相同的,for_ 只有一个更弱的 (Applicative) 约束。 - cchalmers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接