Класс векторных типов Haskell: функция [a] -> [a] -> a

Хорошо, я пытаюсь разобраться с классами типов, поэтому я пытаюсь определить класс типов для операций с геометрическими векторами. Мне удалось заставить его работать по компонентам +,-,*,/;, но я борюсь с точечным продуктом.

class GeomVector a where
  (>+) :: a -> a -> a
  (>-) :: a -> a -> a
  (>*) :: a -> a -> a
  (>/) :: a -> a -> a

  (>.) :: a -> a -> Double

data Vector a = Vec [a]
              deriving Show

instance (Fractional a) => GeomVector (Vector a) where
  (>+) (Vec u) (Vec v) = Vec $ zipWith (+) u v
  (>-) (Vec u) (Vec v) = Vec $ zipWith (-) u v
  (>*) (Vec u) (Vec v) = Vec $ zipWith (*) u v
  (>/) (Vec u) (Vec v) = Vec $ zipWith (/) u v

  (>.) (Vec u) (Vec v) = sum $ u >* v

Очевидно, что мое определение экземпляра для (>.) не будет работать, потому что результат имеет тип Fractional a, а не Double.

Но я не знаю, как получить такое поведение из объявления в классе.

Я хочу сделать следующее:

class GeomVector [a] where
  (>.) :: [a] -> [a] -> a

Но это неверно, потому что [a] — это тип, а не переменная типа.

Я хотел бы объяснить это немного лучше, но я, честно говоря, недостаточно понимаю, чтобы сделать это. Надеюсь, код сделает более очевидным то, с чем я борюсь.


person Mistodon    schedule 04.12.2012    source источник
comment
Я думаю, вам нужна другая переменная типа для обозначения типа скаляров, то есть class GeomVector a s where ... (>.) :: a -> a -> s.   -  person ErikR    schedule 05.12.2012
comment
Вам нужны Синонимы связанных типов   -  person Lambdageek    schedule 05.12.2012
comment
Ваше объявление класса ошибочно не только из-за типа результата (›.). Вы пытаетесь создать точечный продукт u и v, которые являются списками, а не экземплярами вашего класса.   -  person Dmitry Dzhus    schedule 05.12.2012
comment
@Lambdageek Почему синонимы? Разве здесь не нужен только ассоциированный тип? (data Scalar a)?   -  person Dmitry Dzhus    schedule 05.12.2012
comment
@DmitryDzhus Scalar a должен быть каким-то существующим типом (например, для instance GeomVector [a] это должен быть a), а не совершенно новым типом данных.   -  person Lambdageek    schedule 05.12.2012
comment
Взгляните на пакет vector-space.   -  person leftaroundabout    schedule 05.12.2012


Ответы (1)


Вот один из вариантов, который может сработать:

class GeomVector v where
  (>+) :: Num a=> v a -> v a -> v a
  (>-) :: Num a=> v a -> v a -> v a
  (>*) :: Num a=> v a -> v a -> v a
  (>/) :: Fractional a=> v a -> v a -> v a
  (>.) :: Num a=> v a -> v a -> a

data Vector a = Vec { vecList :: [a] }
              deriving Show

instance GeomVector Vector where
  (>+) (Vec u) (Vec v) = Vec $ zipWith (+) u v
  (>-) (Vec u) (Vec v) = Vec $ zipWith (-) u v
  (>*) (Vec u) (Vec v) = Vec $ zipWith (*) u v
  (>/) (Vec u) (Vec v) = Vec $ zipWith (/) u v

  (>.) u v = sum $ vecList (u >* v)

Таким образом, все ваши экземпляры GeomVector будут иметь тип * -> *, подобный классу Monad. И типы методов не ограничены без необходимости Fractional типами только потому, что вы где-то там разделяете.

Вы также можете подумать о том, чтобы сделать свой класс как можно меньше (сделать >. полиморфной функцией вне класса) и решить, действительно ли вам нужен класс типов. Но все это зависит от того, что вы разрабатываете, и я не хочу предполагать, что знаю об этом лучше вас!

person jberryman    schedule 05.12.2012
comment
Вероятно, невозможно переместить >. из класса, так как ему нужно знать о внутренней структуре каждого экземпляра GeomVector для выполнения суммы. - person huon; 05.12.2012
comment
правильно, извините. Таким образом, одним из вариантов было бы сделать класс class (Foldable v)=> GeomVector v, если это имеет смысл. - person jberryman; 05.12.2012
comment
Кроме того, все точечные операции составляют liftA2 для быстрого Applicative, и поэтому Vector может также быть ZipList. - person C. A. McCann; 05.12.2012