Wenn wir die Typen vergleichen
(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m => m s -> (s -> m t) -> m t
Wir bekommen einen Hinweis darauf, was die beiden Konzepte trennt. Das (s -> m t)
in der Art von (>>=)
zeigt, dass ein Wert in s
das Verhalten einer Berechnung in bestimmen kann m t
. Monaden ermöglichen Interferenzen zwischen der Wert- und der Berechnungsebene. Der (<*>)
Operator lässt keine derartigen Störungen zu: Die Funktions- und Argumentberechnungen hängen nicht von Werten ab. Das beißt wirklich. Vergleichen Sie
miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
b <- mb
if b then mt else mf
Dabei wird das Ergebnis eines Effekts verwendet, um zwischen zwei Berechnungen zu entscheiden (z. B. Raketen abschießen und Waffenstillstand unterzeichnen)
iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
cond b t f = if b then t else f
Dabei wird der Wert von verwendet ab
, um zwischen den Werten zweier Berechnungen at
und zu wählenaf
nachdem beide ausgeführt wurden, möglicherweise mit tragischer Wirkung.
Die monadische Version beruht im Wesentlichen auf der zusätzlichen (>>=)
Fähigkeit, eine Berechnung aus einem Wert auszuwählen, und das kann wichtig sein. Die Unterstützung dieser Kraft macht es jedoch schwierig, Monaden zu komponieren. Wenn wir versuchen, 'Doppelbindung' zu erstellen
(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???
wir kommen so weit, aber jetzt sind unsere Schichten alle durcheinander. Wir haben eine n (m (n t))
, also müssen wir das Äußere loswerden n
. Wie Alexandre C sagt, können wir das tun, wenn wir einen geeigneten haben
swap :: n (m t) -> m (n t)
das Innere n
und join
es zum anderen zu permutierenn
.
Die schwächere Doppelanwendung ist viel einfacher zu definieren
(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs
weil es keine Interferenz zwischen den Schichten gibt.
Dementsprechend ist es gut zu erkennen, wann Sie wirklich die zusätzliche Leistung von Monad
s benötigen und wann Sie mit der starren Rechenstruktur davonkommen können, die Applicative
unterstützt.
Beachten Sie übrigens, dass das Komponieren von Monaden zwar schwierig ist, aber möglicherweise mehr als nötig ist. Der Typ m (n v)
gibt an m
, mit n
-Effekten zu rechnen und dann mit -Effekten zu einem -Wert zu rechnen v
, wobei die m
-Effekte enden, bevor die n
-Effekte beginnen (daher ist dies erforderlich swap
). Wenn Sie nur m
-Effekte mit n
-Effekten verschachteln möchten , ist die Komposition vielleicht zu viel verlangt!