Haskell 如何创建围绕可变向量的类型?

Haskell 如何创建围绕可变向量的类型?,haskell,generics,vector,Haskell,Generics,Vector,我试图创建一个类型,一个二维矩阵,它围绕可变向量。我需要一些操作,比如set,get。问题是,我完全不知道如何让它工作的类型。我试过几种方法,但我真的只是在黑暗中拍摄,因为对于如何做到这一点根本没有任何解释。见: data Matrix v s a = Matrix { size :: (Int,Int), buffer :: v s a } -- ?? Wrong newMatrix = ?? set (x,y) mat = ?? get (x,y) val mat = ?? 以…为例。构造

我试图创建一个类型,一个二维矩阵,它围绕可变向量。我需要一些操作,比如set,get。问题是,我完全不知道如何让它工作的类型。我试过几种方法,但我真的只是在黑暗中拍摄,因为对于如何做到这一点根本没有任何解释。见:

data Matrix v s a = Matrix { size :: (Int,Int), buffer :: v s a } -- ?? Wrong
newMatrix = ??
set (x,y) mat = ??
get (x,y) val mat = ??

以…为例。构造器在哪里?在
class MVector v a中
v
a
是什么意思?究竟是什么
m(v(PrimState m)a)
?这是通用可变向量的“类型”吗?什么是
m
v
?PrimState是什么?

正如@user2407038所指出的,一开始更容易将自己限制为一个具体的
MVector
类型,比如
IOVector
。如果您专门化类型,并将其转换为
IOVector
,则会得到以下独特的非恐吓类型:

new   :: Int -> IO (IOVector a)
read  :: IOVector a -> Int -> IO a
write :: IOVector a -> Int -> a -> IO ()
因此,对于第一个版本,矩阵
操作的实现非常简单:

import Data.Vector.Mutable as V
import Control.Monad (liftM)

data Matrix a = Matrix { size :: (Int, Int), buffer :: IOVector a }

newMatrix :: (Int, Int) -> IO (Matrix a)
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h)

set :: (Int, Int) -> a -> Matrix a -> IO ()
set pos e mtx = V.write (buffer mtx) (offset mtx pos) e

get :: (Int, Int) -> Matrix a -> IO a
get pos mtx = V.read (buffer mtx) (offset mtx pos)

offset :: Matrix a -> (Int, Int) -> Int
offset (Matrix (w, _h) _) (x, y) = w * y + x
那么,我们如何在
MVector s
中概括选择
s
<代码>矩阵本身需要在选择
s
时进行概括:

data Matrix s a = Matrix { size :: (Int, Int), buffer :: MVector s a }
我们还需要将这种泛化贯穿到所有函数中。让我们详细了解一下
newMatrix
;其余的可以留给读者作为练习

如果我们只是在
s
上进行抽象,
newMatrix
就变成了

newMatrix :: (Int, Int) -> IO (Matrix s a)
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h) -- Same implementation as above
然而,这肯定是不对的——我们不能在
IO
中为
s
的任何选择创建
MVector s a
,只有
现实世界
!不出所料,typechecker会捕捉到以下信息:

Couldn't match type `s' with `RealWorld'
  `s' is a rigid type variable bound by
      the type signature for newMatrix :: (Int, Int) -> IO (Matrix s a)
Expected type: s
  Actual type: PrimState IO
Expected type: IO (MVector s a)
  Actual type: IO (MVector (PrimState IO) a)
In the return type of a call of `new'
In the second argument of `($)', namely `new (w * h)'
但是假设我们写了

newMatrix :: (Monad m) => (Int, Int) -> m (Matrix s a)
从某种意义上说,这甚至更糟:现在我们说,对于
m
s
(相互独立!)的任何选择,我们都可以在
m
中构造
矩阵sa
。显然情况并非如此

这就是需要typeclass的地方:它提供了
PrimState m
之间的链接,为被操纵的向量选择
s
,以及可以进行这种操纵的monad
m
<代码>新矩阵因此成为

newMatrix :: (PrimMonad m) => (Int, Int) -> m (Matrix (PrimState m) a)
newMatrix (w, h) = liftM (Matrix (w, h)) $ V.new (w * h) -- implementation is still the same!

其他操作可以用类似的方式键入。

newMatrix
必须有一个类型
(PrimMonad m,MVector va)=>arg1->arg2->argN->m(Matrix v s a)
,因为您需要使用
Data.Vector.Generic.Mutable.new
来构造它。
PrimState
是与
PrimMonad
关联的类型。只有两个
PrimMonad
实例:
sts
IO
<因此,code>PrimState要么是
RealWorld
要么是
s
(来自
ST-s
)。但是这些东西都隐藏在里面。注意:你链接的文档是古老的。这是2010年的向量0.5。相反,请看一看,尽管类型仍然基本相同。首先为
IOVector
编写这些函数,然后简单地概括这些类型,使它们在vector type.Brilliant中具有多态性,这可能是一种解释。考虑到互联网上这一主题的现状,我打赌很多人会从你的帖子中学习,这些帖子来自谷歌。