Functor, Applicative und Monad erläutert.
Die Functor Typklasse ist eine der simpelsten Typklassen für Typkonstruktoren und dient als Basis für viele andere Typklassen. Sie hat nur eine Funktion:
class Functor f where
  fmap :: (a -> b) -> f a -> f b
Die Funktion fmap hat zwei Parameter. Der zweite Parameter ist ein Wert der jeweiligen Instanz von Functor. Diese hat den Elementtyp a. Der erste Parameter ist eine Funktion, die einen Wert vom Typ a in einen Wert vom Typ b umwandelt, also in den Elementtypen des Rückgabewertes von fmap. Zur Veranschaulichung hier die Instanz von Functor für Listen und Maybe:
instance Functor [] where
  fmap g [] = []
  fmap g (x:xs) = (g x) : (fmap g xs)
instance Functor Maybe where
  fmap g Nothing = Nothing
  fmap g (Just x) = Just (g x)
Die Semantik von fmap lässt sich auf zwei Arten interpretieren:
fmap wendet eine Funktion auf alle Elemente eines Containers an, ohne die Containerstruktur zu verändern.fmap wendet eine Funktion auf einen Wert an, ohne den Kontext des Wertes zu ändern.Maybe stellt eine Berechnung mit möglichem Fehlschlag dar. Ist der Wert Nothing steht für einen Fehler und eín Wert (Just x) für das erfolgreiches Berechnungsergebnis x.fmap wird deutlich, wenn man weitere Klammern in die Signatur einfügt:
class Functor f where
  fmap :: (a -> b) -> (f a -> f b)
Diese Signatur ist äquivalent zur ursprünglichen Signatur von fmap. Die Funktion fmap nimmt als Parameter ein Funktion a -> b und wandelt diese ein eine Funktion f a -> f b um. Die neue Funktion arbeitet im Kontext von des Funktors f. Dieser Vorgang wird häufig als liften von Funktionen bezeichnet.
Jede Instanz von Functor sollte die folgenden Gesetze erfüllen:
fmap id = id
fmap (g . h) = fmap g . fmap h
Diese beiden Gesetze stellen die strukturerhaltenden Eigenschaften, die schon in den beiden Semantiken von fmap angesprochen wurden, sicher, und bieten gleichzeitig einen Anstoß zur Optimierung. Wenn bei einer Datenstruktur das Traversieren mit fmap sehr aufwändig ist und der Aufwand des Anwendens der Funktion, die fmap übergeben wird, sehr gering, lassen sich zwei Aufrufe von fmap mit den Funktionen g und h zu einem Aufruf von fmap mit der Funktion (g . h) zusammenfassen.
Der Typ ((,) e) entspricht einem Paar dessen erstes Element den Typ e hat. Dieser Wert vom Typ e stellt eine Anmerkung, oder möglicherweise eine Fehlermeldung zum Wert des zweiten Elementes dar. Dieser Typ lässt sich unter Zuhilfenahme einer weiteren Typklasse zur so genannten Writer Monade ausbauen.
instance Functor ((,) e) where -- entspricht sinngemäß "(e,)" ist aber leider syntaktisch nicht möglich
  fmap g (e,x) = (e,g x)
Der Wertebereich des Typkonstruktors ((->) r) umfasst alle einstelligen Funktionen die Werte vom Typ r akzeptieren und einen Wert eines beliebigen Typen zurückgeben. Ein konkreter Wert vom Typ (r -> a) kann als Menge von Werten vom Typ a angesehen werden, auf die durch Werte vom Typ r indiziert zugegriffen werden kann. ((->) r) wird auch als Reader Monade bezeichnet.
instance Functor ((->) r) where -- entspricht sinngemäß "(r ->)" ist aber leider syntaktisch nicht möglich
  fmap = (.)
Die Typklasse Applicative erweitert die Typklasse Functor um zwei Funktionen:
class Functor f => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
Die erste Funktion pure nimmt einen Wert vom Typ a und setzt ihn in einen Kontext vom Typ f. Das ist meistens gleichbedeutend mit dem erzeugen eines Conatiners mit genau einem Element. Die zweite Funktion (<*>) ermöglicht es eine Funktion die in einem Kontext f steht auf einen, ebenfalls in einem Kontext stehenden, Wert a anzuwenden.
Neben den oben genannten Funktionen stellt die Typklasse Applicative die Funktion (<$>) bereit, welche die Infixvariante von fmap ist.
Für drei der bisher vorgestellten Instanzen von Functor lassen sich Instanzen von Applicative bilden:
instance Applicative [] where
  pure x = [x]
  gs <*> xs = [g x | g <- gs, x <- xs]
Die Implementierung von pure für Listen erzeugt lediglich eine einelementige Liste. (<*>) erwartet eine Liste von Funktionen vom Typ a -> b und eine Liste von Werten vom Typ a. Das ergebnis von (<*>) ist eine Liste aller Kombination von Funktionsanwendungen der ersten Liste auf die Elemente der zweiten Liste und demnach von Typ [b]. Dies ermöglicht eine nichtdeterministische Berechnung in eleganter Notation:
> (+) <$> [1,2,3] <*> [10,20,30]
[11,21,31,12,22,32,13,23,33]
<$> (entspricht fmap) die Liste [1,2,3] in eine Liste von Funktionen umgewandelt: [(1+),(2+),(3+)].<*> Paare aus den Beiden Listen gebildet: [(1+)10,(1+)20,(1+)30,(2+)10,(2+)20,(2+)30,(3+)10,(3+)20,(3+)30][11,21,31,12,22,32,13,23,33]
instance Applicative Maybe where
  pure x = Just x
  _ <*> Nothing = Nothing
  Nothing <*> _ = Nothing
  (Just g) <*> (Just x) = Just $ g x
Für den Datentyp Maybe enthält die Implementierung von Applicative im wesentlichen nur drei Fälle für (<*>). Bei Abwesenheit (Nothing) der Funktion, oder des Wertes ist das Ergebnis von (<*>) auch Nothing. Die Implementierung von pure lässt nur diese eine Möglichkeit zu, was später noch durch die Gesetze der Typklasse Applicative verdeutlicht wird.
instance Applicative ((->) r) where
  pure x = const x
  g <*> h = (\r -> g r $ h r)
pure wird durch eine einstellige Funktion implementiert, die unabhängig von der Eingabe immer den gleichen Wert x liefert. Durch die Implementierung von <*> werden zwei Reader Monaden zu einer verkettet.
fmap g x = pure g <*> x
bzw.
g <$> x = pure g <*> x
Das Liften einer Funktion g durch fmap und Anwenden auf einen kontextbehafteten Wert x ist identisch  mit dem anwenden von <*> auf eine Funktion, die mit pure in einen Kontext gehoben wurde und einem kontextbehafteten Wert. So erklärt sich auch die Namensgebung der Funktion pure. Die Anwendung eines durch pure erzeugten Apllicative verändert den Kontext des zweiten Apllicative nicht.
Die Typklasse Monad sieht auf den ersten Blick wie der Beginn einer neuen Hierarchie von Typklassen aus:
class Monad m where
  return :: a -> m a
  (>>=)  :: m a -> (a -> m b) -> m b
Bei genauerer Betrachtung fällt jedoch schnell auf, dass die Signatur von return identisch mit Signatur der bereits vorgestellten  Funktion pure aus der Applicative Typklasse ist. Die Funktion return hat die selbe Aufgabe wie pure und deshalb auch die selbe Implementierung.
Die Signatur der Funktion (>>=) (auch bind genannt) zeigt die eigentliche Neuerung gegenüber der Typklasse Applicative. Weiterhin wird ein kontextbefafteter Wert vom Typ m a in einer kontextbehafteten Wert vom Typ m b überführt. Diesmal jedoch wird der neue Kontext nicht nur durch die Implementierung der Typklasse, sondern auch noch durch die als zweiter Parameter übergebene Funktion vom Typ a -> m b, beeinflusst. Die Aufgabe der Funktion (>>=) ist es also, den Wert aus seinem Kontext zu lösen und mit Hilfe der übergebenen Funktion einen neuen Kontext sammt Wert zu erzeugen.
Für alle hier vorgestellten Instanzen von Applicative lassen sich Instanzen für Monad bilden:
instance Monad [] where
  return = pure
  [] >>= _ = []
  (x:xs) >>= g = (g x) ++ (xs >>= g)
  -- Alternative mit fmap
  -- xs >>= g = concat $ fmap g xs
Die Implementierung von return ist identisch mit der von pure. Diese Implementierung kann bei allen Instanzen von Monad, die auch Applicativ implementieren, verwendet werden.
Die Funktion >>= erhält als ersten Parameter eine Liste mit Werten vom Typ a. Als zweiten Parameter eine Funktion g vom Typ a -> [b]. Die Funktion g wird elementweise auf die Liste angewendet und die so entstehenden Ergebnislisten konkateniert.
instance Monad Maybe where
  return = pure
  (Just x) >>= g = g x
  Nothing  >>= _ = Nothing
Für den Fall, dass der Wert Nothing ist gibt die Funktion >>= den Wert Nothing zurück, sonst das Funktionsergebnis von g x. So lassen sich jetzt Berechnungen mit Fehlern darstellen:
Just 3
>>= (\x -> Just (x + 1))
>>= (\y -> if odd y then Just y else Nothing)
>>= (\z -> Just (z - 1))
Just 3 dient als Startwert der Berechnung.3 an x. Die Funktion liefert das Ergebnis Just 4.4 an y. Die Funktion liefert das Ergebnis Nothing.Nothing als Ergebnis der Berechnung.
instance Monad ((->) r) where
  return = pure
  g >>= h = (\r -> h (g r) r)
Der bind-Operator verkettet, ähnlich wie bei der Implementierung von Applicative mehrere Reader zu einem. Eine sehr einfacher Wert für einen Reader ist eine Funktion auf natürlichen Zahlen:
(*3)
>>= (\i -> if odd i then return i else return 0)
>>= (\i' -> return (i' `div` 3))
$ 3
(*3), die alle Zahlen auf ihr Dreifaches abbildet.0 abbildet.3) aufgerufen wird. Das Ergebnis der Berechnung ist 3.
Wie anfangs angedeutet Besteht nur auf den ersten Blick kein Zusammenhang zwischen Monad und Applicative. Die fehlenden Hierarchie der Typklassen ist jedoch auf die Historie von Haskell zurückzuführen, da es Monad bereits vor Applicative gab.
Um die Hierarchie etwas deutlicher zu machen, hier ein paar Hilfsfunktionen:
liftM :: Monad m => (a -> b) -> m a -> m b
liftM g m = m >>= (\x -> return (g x))
Die Funktion liftM hebt eine Beliebige Funktion vom Typ a -> b in einen Monadischen Kontext. Dazu wird zuerst der Wert der sich in der Monade befindet mit Hilfe des bind-Operators geholt, dann die Funktion g darauf angewendet und schließlich das Ergebnis mittels return wieder in den monadischen Kontext gesetzt. Die Funktion liftM bietet somit die gleiche Funktionalität wie fmap aus der Typklasse Functor.
ap :: Monad m => m (a -> b) -> m a -> m b
ap mg m = mg >>= (\g -> m >>= (\x -> return (g x)))
Die Funktion ap ermöglicht es eine Funktion, die in einem monadischen Kontext steht auf einen monadischen Wert anzuwenden. Sie bildet das Pendant zur Funktion <*> aus der Typklasse Applicative.
Es lassen sich zu jeder Instanz von Monad Instazen von Functor und Applicative erzeugen, indem fmap als liftM, pure als return und <*> als ap definiert werden.
join :: Monad m => m (m a) -> m a
join mm = mm >>= id
Wenn der Inhalt einer Monade wieder eine Monade ist, kann es hilfreich sein diese Zusätzliche Schicht entfernen zu können. Die Funktion join übernimmt genau diese Aufgabe.
Unter Zuhilfenahme dieser drei Funktionen lässt sich nun eine alternative Definition der Typklasse Monad aufstellen:
class Applicative m => Monad' m where
  return :: a -> m a
  return = pure
  
  (>>=) :: m a -> (a -> m b) -> m b
  m >>= g = join $ fmap g m
  join :: m (m a) -> m a
  join mm = mm >>= id
Durch das Hinzufügen der Klassenhierarchie zu Apllicative ist es nun möglich return durch pure zu implementieren. Außerdem ist jetzt die Defition von >>= durch fmap und join möglich. Durch die zyklische Definition von >>= und join würde es ausreichen eine der beiden Funktionen zu implementieren um die volle Funktionalität von Monad zu erhalten. Dies ist besonders reizvoll, da die Definition von join meistens intuitiver und leichter verständlich ist:
instance Monad' Maybe where
  join Nothing = Nothing
  join (Just x) = x
instance Monad' [] where
  join = concat
instance Monad ((->) e) where
  join mm = \e -> mm e e