2014-12-24 1 views
2

Я пытаюсь создать тип, 2D-матрицу, которая обертывает вокруг изменяемых векторов. Я хочу несколько операций, таких как 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 = ?? 

Возьмите, например, the official documentation. Где конструктор? Что делает v и a в class MVector v a where означает? Какая черта m (v (PrimState m) a)? Это «тип» универсального изменяемого вектора? Что такое m и v? Что такое PrimState?

+2

'newMatrix' должен будет иметь тип' (PrimMonad m, MVector va) => arg1 -> arg2 -> argN -> m (Matrix vsa) ', так как вам нужно будет использовать' Data. Vector.Generic.Mutable.new', чтобы его построить. – bheklilr

+0

'PrimState' - это тип, связанный с' PrimMonad'. Есть только два экземпляра 'PrimMonad':' ST s' и 'IO'. Следовательно, 'PrimState' является либо' RealWorld', либо 's' (из' ST s'). Но эти вещи скрыты в ['Control.Monad.Primitive'] (http://hackage.haskell.org/package/primitive-0.2.1/docs/Control-Monad-Primitive.html#t:PrimMonad). – Zeta

+0

Примечание: документация, которую вы связываете, является древней. Это 'vector 0.5' с 2010 года. Посмотрите на [' vector 0.10.12. * '] (Http://hackage.haskell.org/package/vector-0.10.12.2/docs/Data-Vector-Mutable.html), хотя тип по-прежнему в основном один и тот же. – Zeta

ответ

3

Как отметил пользователь @ user2407038, сначала проще ограничиться бетоном MVector типа IOVector. Если вы специализируетесь типа new, read и write к 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 

Итак, как мы обобщаем выбор s в MVector s? Matrix сами должен быть обобщены по выбору 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 

Однако, конечно, это не может быть правильным - мы не можем создать MVector s a в IO при любом выборе s, только 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) 

Это, в некотором смысле, даже хуже: теперь мы говорим, что при любом выборе m и s (независимо от друг друга!), мы можем построить Matrix s a в m. Ясно, что это не так.

Это где необходим PrimMonad класс типов: она обеспечивает связь между PrimState m, выбором s для вектора манипулирует, и монада m, где эта манипуляция возможна. newMatrix таким образом, становится

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! 

Остальные операции могут быть набраны в аналогичным образом.

+0

Блестящий. Учитывая состояние предмета в Интернете, я готов поспорить, что многие из вас будут учиться на вашем посту, поступающем из Google. – MaiaVictor

Смежные вопросы