Typeclassopedia
Funktoren, Monaden, Arrows: Typklassen für Typkonstruktoren

von Robert Steuck


Einleitung | Grundlagen | Vom Funktor zur Monade | Monoid | Arrows | Quellenverzeichnis

Monoid

Die Typklasse Monoid bietet eine Schnittstelle, zur Implementation von Monoiden auf Haskell Datentypen. Ein Monoid besteht aus einer Menge, einer zweistelligen, inneren, assoziativen Operation auf der Menge, für die ein neutrales Element aus der Menge existiert.


class Monoid a where
  mempty :: a
  mappend :: a -> a -> a

Die Menge wird durch die Werte des Datentyps a gegeben, mappend ist die Operation und mempty ihr neutrales Element. Die Namensgebung für die Funktionen dieser Typklasse sind an die Implementierung ihrer Instanz für Listen unter Konkatenation angelehnt:


instance Monoid [a] where
  mempty = []
  mappend = (++)

Im Gegensatz zur Funktor, oder Monad Instanz für Listen, ist der Typ dieser Instanz kein Typkonstruktor. Dies wird auch dadurch deutlich, dass die Typvariable a nur an normaler Parameterposition in den Signaturen der Typklasse Monoid auftritt.


Weitere Instanzen von Monoid

Es existieren zwei newtype-Wrapper für Zahlen um die Monoide unter Addition und 0, sowie Multiplikation und 1 abzubilden:


newtype Sum a = Sum {getSum :: a}

instance (Num a) => Monoid (Sum a) where
  mempty = Sum 0
  (Sum x) `mappend` (Sum y) = Sum (x + y)

newtype Product a = Product {getProduct :: a}

instance (Num a) => Monoid (Product a) where
  mempty = Product 1
  (Product x) `mappend` (Product y) = Product (x * y)

Auch für den Datentyp Bool sind zwei Instanzen von Monoid vordefiniert:


newtype Any = Any {getAny :: Bool}

instance Monoid Any where
  mempty = Any False
  (Any x) `mappend` (Any y) = Any (x || y)

newtype All = All {getAll :: Bool}

instance Monoid All where
  mempty = All True
  (All x) `mappend` (All y) = All (x && y)

Gesetze

Jede Instanz von Monoid sollte folgende Gesetze befolgen:


mempty `mappend` x = x
x `mappend` mempty = x
(x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)

Die erste beiden Gleichungen garantieren mempty als neutrales Element zur Funktion mappend. Die letzte Gleichung beschreibt die Assoziativität von mappend.


Writer Monade

Unter Nutzung der Typklasse Monoid kann aus dem Typkonstruktor ((,) e) eine Instanz von Applicative, sowie Monad gemacht werden:


instance Monoid e => Applicative ((,) e) where
  pure x = (mempty, x)
  (u,f) <*> (v,x) =  (u `mappend` v, f x)

instance Monoid e => Monad ((,) e) where
  return = pure
  (u,x) >>= g = (u `mappend` v, y)
    where
      (v,y) = g x

Dies war vorher nicht möglich, da für die Implementierung von pure ein Startwert vom Typ e fehlte. Durch das hinzufügen der Bedingung, dass e eine Instanz von Monoid sein muss, ist dies nun möglich. Wird z.B. für den Typ des ersten Elementes [String] gewählt, können Bemerkungen, oder Zwischenergebnisse einer Berechnung, wie in einem Protokoll gesammelt werden:


return 0
>>= (\x -> (["x + 1"],x+1))
>>= (\x -> (["1 div x","evtl. 1 div 0"],1 `div` x))
>>= (\x -> (["x * 100"], 100 * x))

Als Startwer dient return 0, was ([],0) entspricht. Der Wert des zweiten Elementes des Paares wird mit >>= durch die drei Rechenoperationen geschleift. Die Strings in den Listen des ersten Elementes der Funktionsresultate werden akkumuliert. Das Ergebnis der Berechnung ist:


(["x + 1",
  "1 div x",
  "evtl. div 0",
  "x * 100"],
 100)


Andere monoide Typklassen

Es gibt neben der Typklasse Monoid noch weitere Typklassen die eine monoide Struktur haben und deren Instanzen die Monoidgesetze befolgen sollten:


class Applicative f => Alternative f where
  empty :: f a
  (<|>) :: f a -> f a -> f a

Durch die Typklasse Alternative wird Applicative um die Funktion der Auswahl erweitert.


class Monad m => MonadPlus m where
  mzero :: m a
  mplus :: m a -> m a -> m a

Auch Monad lässt sich mit Hilfe von MonadPlus um diese Funktion erweitern.


Einleitung | Grundlagen | Vom Funktor zur Monade | Monoid | Arrows | Quellenverzeichnis