如何创建环绕可变向量的类型



我正在尝试创建一个类型,一个环绕可变向量的 2D 矩阵。我想要一些操作,例如设置,获取。问题是我完全不知道如何让它工作的类型。我已经尝试了几件事,但我真的只是在黑暗中拍摄,因为绝对没有关于如何做到这一点的解释。看:

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 where中的va是什么意思?m (v (PrimState m) a)到底是什么?这是通用可变向量的"类型"吗?什么是mv?PrimState到底是什么?

正如@user2407038所指出的,一开始将自己限制在像IOVector这样的具体MVector类型更容易。如果你专注于new的类型,readwrite IOVector,你会得到以下独特的非恐吓类型:

new   :: Int -> IO (IOVector a)
read  :: IOVector a -> Int -> IO a
write :: IOVector a -> Int -> a -> IO ()

因此,对于第一个版本,Matrix操作的实现非常简单:

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 ss的选择呢? Matrix本身需要在s的选择上推广:

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

我们还需要将这种泛化贯穿到所有函数中。让我们详细看看newMatrix;其余的可以留给读者练习。

如果我们只是抽象snewMatrix变成

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

然而,这肯定是不对的——我们不能为任何s选择IO创造MVector s a,只有RealWorld!意料之中的是,类型检查器捕获了以下内容:

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)

从某种意义上说,这甚至更糟:现在我们说,对于ms(彼此独立!)的任何选择,我们都可以在m中构建一个Matrix s a。显然情况并非如此。

这就是需要PrimMonad类型类的地方:它提供了PrimState m之间的链接,为纵的向量选择s,以及可以进行这种操作的monad mnewMatrix因此成为

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!

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

最新更新