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.
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)
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
.
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)
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.