Jeśli porównamy typy
(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m => m s -> (s -> m t) -> m t
otrzymujemy wskazówkę, co oddziela te dwa pojęcia. To (s -> m t)w typie (>>=)pokazuje, że wartość w smoże określać zachowanie obliczenia w m t. Monady pozwalają na interferencję między warstwami wartości i obliczeń. (<*>)Operator nie umożliwia takich zakłóceń: Działanie i argumentów obliczenia nie zależy od wartości. To naprawdę gryzie. Porównać
miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
b <- mb
if b then mt else mf
który wykorzystuje wynik jakiegoś efektu do wyboru między dwoma obliczeniami (np. wystrzelenie rakiet i podpisanie rozejmu), podczas gdy
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
która używa wartości abdo wyboru między wartościami dwóch obliczeń ati af, po przeprowadzeniu obu, być może z tragicznym skutkiem.
Wersja monadyczna polega zasadniczo na dodatkowej mocy (>>=)wyboru obliczeń z wartości, a to może być ważne. Jednak wspieranie tej mocy utrudnia komponowanie monad. Jeśli spróbujemy zbudować „podwójne wiązanie”
(>>>>==) :: (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 ???
dotarliśmy tak daleko, ale teraz wszystkie nasze warstwy są pomieszane. Mamy n (m (n t)), więc musimy pozbyć się zewnętrznego n. Jak mówi Alexandre C, możemy to zrobić, jeśli mamy odpowiedni
swap :: n (m t) -> m (n t)
permutować do nwewnątrz i joindo drugiego n.
Słabsze „podwójne zastosowanie” jest znacznie łatwiejsze do zdefiniowania
(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs
ponieważ nie ma interferencji między warstwami.
W związku z tym dobrze jest rozpoznać, kiedy naprawdę potrzebujesz dodatkowej mocy Monads i kiedy możesz uciec ze sztywną strukturą obliczeniową, która Applicativeobsługuje.
Zwróć uwagę, że chociaż komponowanie monad jest trudne, może to być więcej niż potrzebujesz. Typ m (n v)wskazuje na obliczanie z m-efektami, a następnie obliczanie z n-efekty do -wartości v, gdzie m-efekty kończą się przed nrozpoczęciem -efekty (stąd potrzeba swap). Jeśli chcesz po prostu przeplatać m-efekty z n-efektami, to o skład może zbyt wiele prosić!