Własny typ danych jako instancja klasy w Haskellu

0

Mam w Haskellu zdefiniowaną następującą klasę (półgrup z jedynką):

class Monoid a where 
  (***) :: a -> a -> a
  e :: a
infixl 6 ***

Jeśli chciałbym aby Integer był instancją powyższej klasy to nie ma z tym problemu. Mamy np.

instance Monoid Integer where 
        e = 1
        (***) x y = mod (x*y) (10)

reprezentuje Z_9 z działaniem mnożenia modulo.

Oprócz tego mam zdefiniowany własny typ danych, który reprezentuje macierze 2x2:

 data Mtx2x2 a = Mtx2x2 a a a a deriving(Show) 

Chciałbym, aby takie macierze również były instancją klasy Monoid, w tym celu robię coś takiego:

instance Monoid (Mtx2x2 Int) where 
          ...

Niestety, powyższa konstrukcja zwraca mi błąd:

Lista10.hs10:
Illegal instance declaration for Monoid (Mtx2x2 Int)' (All instance types must be of the form (T a1 ... an) where a1 ... an are *distinct type variables*, and each type variable appears at most once in the instance head. Use -XFlexibleInstances if you want to disable this.) In the instance declaration for Monoid (Mtx2x2 Int)'

Kombinowałem na kilka innych sposobów, ale kod się nie kompiluje. Chciałbym to zrozumieć, więc byłbym wdzięczny za wytłumaczenie lub odesłanie do odpowiednich materiałów.

1

Przecież w komunikacie o błędzie masz dokłądnie, krok po kroku wytłumaczone co, dlaczego oraz co możesz zrobić. Kombinowanie na kilka sposobów nic nie da jeśli nie będziesz wiedział co konkretnie jest źle.

Illegal instance declaration for Monoid (Mtx2x2 Int)'
(All instance types must be of the form (T a1 ... an)
where a1 ... an are distinct type variables,
and each type variable appears at most once in the instance head.

Use -XFlexibleInstances if you want to disable this.)
In the instance declaration for Monoid (Mtx2x2 Int)'

0

Ok, ale co to jest XFlexibleInstances ?

0

Ok już chyba sobie poradziłem. Gdyby ktoś spotkał się z podobnym problemem to tu jest pomoc:

http://connectionrequired.com/blog/2009/07/my-first-introduction-to-haskell-extensions-flexibleinstances/

Należy pamiętać, że wyrażenie

 {-# LANGUAGE FlexibleInstances #-} 

musi być na samym początku pliku (nawet przed wszelkimi importowanymi modułami).

1

Dokładnie.

To rozwiązanie ma tą zaletę że jest ładne, wadą jest to że {-# LANGUAGE ... #-} to rozszerzenia GHC, nieobecne w standardowym Haskell98.

Klasyczne rozwiązanie, bez rozszerzeń które zadziała wszędzie (ale jest niepotrzebnie skomplikowane) to (trochę uproszczona deklaracja macierzy dla demonstracji):

data Mtx a = Mtx a deriving(Show) 

newtype IntMtx = IntMtx (Mtx Int)

instance Monoid IntMtx where
    IntMtx (Mtx a) *** IntMtx (Mtx b) = IntMtx $ Mtx $ a * b
    e = IntMtx (Mtx 1)

Czyli otoczenie Mtx2x2 Int w newtype.

*Main> IntMtx (Mtx 2) *** IntMtx (Mtx 3)
IntMtx (Mtx 6)

1 użytkowników online, w tym zalogowanych: 0, gości: 1