Определение модуля алгебры с помощью пакета конструктивной алгебры

Пакет constructive-алгебра позволяет вам определять экземпляры алгебраических модулей (таких как векторные пространства, но с использованием кольца, где поле было обязательным)

Это моя попытка определить модуль:

{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-}
module A where
import Algebra.Structures.Module
import Algebra.Structures.CommutativeRing
import Algebra.Structures.Group

newtype A = A [(Integer,String)]

instance Group A where
    (A a) <+> (A b) = A $ a ++ b
    zero = A []
    neg (A a) = A $ [((-k),c) | (k,c) <-  a]


instance Module Integer A where
    r *> (A as) = A [(r <*> k,c) | (k,c) <- as]

Это терпит неудачу:

A.hs:15:10:
    Overlapping instances for Group A
      arising from the superclasses of an instance declaration
    Matching instances:
      instance Ring a => Group a -- Defined in Algebra.Structures.Group
      instance Group A -- Defined at A.hs:9:10-16
    In the instance declaration for `Module Integer A'

A.hs:15:10:
    No instance for (Ring A)
      arising from the superclasses of an instance declaration
    Possible fix: add an instance declaration for (Ring A)
    In the instance declaration for `Module Integer A'
Failed, modules loaded: none.

Если я закомментирую экземпляр Group, то:

A.hs:16:10:
    No instance for (Ring A)
      arising from the superclasses of an instance declaration
    Possible fix: add an instance declaration for (Ring A)
    In the instance declaration for `Module Integer A'
Failed, modules loaded: none.

Я прочитал это как требующее, чтобы экземпляр Ring A имел Module Integer A, что не имеет смысла и не требуется в определении класса:

class (CommutativeRing r, AbelianGroup m) => Module r m where
  -- | Scalar multiplication.
  (*>) :: r -> m -> m

Не могли бы вы объяснить это?


person user21338    schedule 10.05.2012    source источник
comment
Вы обеспокоены тем, что ваш экземпляр Group для A на самом деле не определяет группу? Например, let a = [(1,"foo")], затем a <+> neg a = [(1,"foo"),(-1,"foo")], что не совпадает с zero.   -  person Chris Taylor    schedule 10.05.2012
comment
Да, я знаю. Первоначальная идея заключалась в том, чтобы уменьшить a ++ b путем группировки по идентичным строкам. Я пропустил приведение к нормальной форме, чтобы упростить пример.   -  person user21338    schedule 10.05.2012


Ответы (2)


Пакет содержит

instance Ring a => Group a where ...

Заголовок экземпляра a соответствует каждому выражению типа, поэтому любой экземпляр с любым другим выражением типа будет перекрываться. Это перекрытие вызывает ошибку только в том случае, если такой экземпляр действительно где-то используется. В вашем модуле вы используете экземпляр в

instance Module Integer A where
    r *> (A as) = A [(r <*> k,c) | (k,c) <- as]

Класс Module имеет ограничение AbelianGroup на параметр m¹. Это подразумевает ограничение Group. Таким образом, для этого экземпляра необходимо найти Group экземпляр A. Компилятор находит два совпадающих экземпляра.

Это первая зарегистрированная ошибка.

Во-вторых, компилятор пытается найти экземпляр AbelianGroup для A. Единственный экземпляр, о котором компилятор знает в этот момент, это

instance (Group a, Ring a) => AbelianGroup a

поэтому он пытается найти instance Ring A where ..., но, конечно, его нет.

Вместо того, чтобы комментировать instance Group A where ..., вы должны добавить

instance AbelianGroup a

(даже если это ложь, мы просто хотим, чтобы она скомпилировалась в данный момент), а также добавить OverlappingInstances к прагме
{-# LANGUAGE #-}.

С OverlappingInstances выбирается наиболее конкретный соответствующий экземпляр, поэтому здесь он делает то, что вы хотите.

¹ Между прочим, ваш A не является экземпляром AbelianGroup и по праву не может им быть, если порядок в списке [(Integer,String)] не имеет значения.

person Daniel Fischer    schedule 10.05.2012
comment
Теперь моя прагма тоже содержит OverlappingInstances. Я добавил instance AbelianGroup A в тело, но все равно получил Overlapping instances for Group A и Overlapping instances for AbelianGroup A - person user21338; 10.05.2012
comment
Какая версия gc? Аналогичный случай компилируется здесь с ghc ›= 7.2. Для ghc ‹= 7.0 модуль, определяющий Group и AbelianGroup, также нуждается в OverlappingInstances, но это будет означать изменение пакета. - person Daniel Fischer; 10.05.2012
comment
Это ghc 7.0.4, и да: я добавил OverlappingInstances к Group.hs, и теперь он компилируется, как задумано. Большое спасибо! - person user21338; 11.05.2012

Этот тип проверяет без неприятных языковых расширений.

{-# LANGUAGE MultiParamTypeClasses, TypeSynonymInstances #-}
module A where
import Algebra.Structures.Module
import Algebra.Structures.CommutativeRing
import Algebra.Structures.Group

newtype A = A [(Integer,String)]

instance Ring A where 
  A xs <+> A ys = A (xs ++ ys)
  neg (A a) = A $ [((-k),c) | (k,c) <-  a]
  A x <*> A y = A [b | a <- x, b <- y ]
  one = A []
  zero = A []

instance Module Integer A where
     r *> (A as) = A [(r <*> k,c) | (k,c) <- as] 

Немного сбивает с толку то, что <+> <*> и neg определены независимо в Ring и Group; это совершенно отдельные символы, но затем они объединяются в общий экземпляр, который делает все Rings Groups, поэтому, если Ring определено, Group не должно быть определено, так как оно уже сказано. Я не уверен, что это навязано автору тем, как работает система классов типов. Module требует Ring, а точнее CommutativeRing. CommutativeRing просто переименовывает Ring; больше ничего не нужно определять. Предполагается, что вы обяжете вас тому, что в Haskell является непроверенным утверждением коммутативности. Таким образом, вы должны, так сказать, «доказать законы CommutativeRing» вне модуля, прежде чем создавать экземпляр Module. Обратите внимание, однако, что эти законы выражены в предложениях быстрой проверки, поэтому вы должны запускать быструю проверку на propMulComm и propCommutativeRing, специализированных для этого типа.

Не знаю, что делать с единицей и нулем, но вы можете пройти мимо порядка, используя подходящую структуру, например:

 import qualified Data.Set as S

 newtype B = B {getBs :: S.Set (Integer,String) }

Но с новым типом вы также можете, например, переопределить Eq для A, чтобы понять это, я полагаю. На самом деле вам нужно запустить предложения быстрой проверки.


Изменить: вот версия с дополнительными материалами, необходимыми для QuickCheck http://hpaste.org/68351, вместе с "Failed " и "ОК" для быстрой проверки для разных экземпляров Eq. Этот пакет кажется мне довольно разумным; Я думаю, вам следует переопределить модуль, если вы не хотите заниматься бизнесом с кольцом и коммутативным кольцом, поскольку он говорит, что «рассматривает только коммутативный случай, вместо этого можно было бы реализовать левый и правый модули». В противном случае вы не сможете использовать quickcheck, который, очевидно, является основным моментом пакета, теперь, когда я вижу, в чем дело, и который он невероятно упростил. Поскольку это именно то, что он пытается исключить с помощью повсеместного использования быстрой проверки, которую, безусловно, было бы очень трудно обмануть в таком случае.

person applicative    schedule 10.05.2012
comment
Я предпочитаю оставлять (<*>) и one как undefined, потому что я не хочу, чтобы A имел кольцевую структуру, и таким образом я получаю сообщение об ошибке всякий раз, когда пытаюсь его использовать. - person user21338; 10.05.2012