Aplikatory komponują, monady nie

Aplikatory komponują, monady nie.

Co oznacza powyższe stwierdzenie? A kiedy jedno jest lepsze od drugiego?

Author: imz -- Ivan Zakharyaschev, 2011-08-12

6 answers

Jeśli porównamy typy

(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m =>       m s -> (s -> m t) -> m t

Dostajemy wskazówkę, co dzieli te dwa pojęcia. To (s -> m t) w typie (>>=) pokazuje, że wartość w s może określić zachowanie obliczeń w m t. Monady pozwalają na interferencję między warstwami wartości i obliczeń. Operator (<*>) nie dopuszcza takiej ingerencji: obliczenia funkcji i argumentów nie zależą od wartości. To naprawdę gryzie. Porównaj

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 decydowania pomiędzy dwa obliczenia (np. odpalenie pocisków i podpisanie rozejmu), natomiast

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óry wykorzystuje wartość ab do wyboru pomiędzy wartości dwóch obliczeń at i af, Po wykonaniu obu, być może w tragiczny sposób.

Wersja monadyczna opiera się zasadniczo na dodatkowej mocy (>>=), aby wybrać obliczenia z wartości, i to może być ważne. Jednak wspieranie tej mocy sprawia, że monady są trudne do skomponowania. Jeśli spróbujemy zbudować "double-bind"

(>>>>==) :: (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 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 odpowiednie
swap :: n (m t) -> m (n t)

Aby permutować n do wewnątrz i join do 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.

Odpowiednio, dobrze jest rozpoznać, kiedy naprawdę potrzebujesz dodatkowej mocy Monad s, a kiedy ujdzie ci to na sucho z sztywną strukturą obliczeniową, która wspiera Applicative.

Zauważ przy okazji, że chociaż komponowanie monad jest trudne, może być więcej niż potrzebujesz. Typ m (n v) oznacza obliczanie za pomocą m-effects, a następnie obliczanie za pomocą n-effects do v-value, gdzie m-effects kończy się przed rozpoczęciem n-effects (stąd potrzeba swap). Jeśli tylko chcesz przeplatać m - efekty z n-efekty, to kompozycja to chyba za dużo!

 118
Author: pigworker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-12 15:44:52

Aplikatory komponują, monady nie.

Monady komponują , ale wynik może nie być monadą. Natomiast skład dwóch aplikantów jest koniecznie aplikatorem. Podejrzewam, że intencją oryginalnego stwierdzenia było to, że "Applicativeness composes, while monadness doesn' t. "przeredagowane," Applicative jest zamknięty pod kompozycją, a Monad nie jest."

 78
Author: Conal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-15 20:20:13

Jeśli masz applicatives A1 i A2, wtedy typ data A3 a = A3 (A1 (A2 a)) jest również applicative (możesz napisać taką instancję w sposób ogólny).

Z drugiej strony, jeśli masz monady M1 i M2, to typ data M3 a = M3 (M1 (M2 a)) niekoniecznie jest monadą (nie ma sensownej implementacji generycznej dla >>= lub join dla kompozycji).

Przykładem może być Typ [Int -> a] (tutaj tworzymy konstruktor typu [] z (->) Int, z których oba są monadami). Możesz łatwo napisz

app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x

I to uogólnia się na dowolne zastosowanie:

app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)

Ale nie ma sensownej definicji

join :: [Int -> [Int -> a]] -> [Int -> a]

Jeśli nie jesteś przekonany do tego, rozważ to wyrażenie:

join [\x -> replicate x (const ())]

Długość zwracanej listy musi być ustawiona w kamieniu przed podaniem liczby całkowitej, ale prawidłowa jej długość zależy od liczby całkowitej, która została podana. Tak więc dla tego typu nie może istnieć żadna poprawna funkcja join.

 40
Author: Rotsor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-05 04:33:25
[1]} niestety, naszym prawdziwym celem, składem monad, jest raczej trudne. .. W rzeczywistości, my może faktycznie udowodnić, że w pewnym sensie nie ma sposobu na zbuduj funkcję join z typem powyżej używając tylko działania dwóch monad (zob. w załączniku zarys dowód). Wynika z tego, że jedynym sposobem, w jaki możemy mieć nadzieję stworzyć skład jest jeśli istnieją jakieś dodatkowe konstrukcje łączące dwa składniki.

Komponowanie monady, http://web.cecs.pdx.edu/ ~ mpj/RR-1004.pdf

 18
Author: Landei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-12 13:50:05

Rozwiązanie prawa dystrybucyjnego l: mn - > NM wystarczy

Aby zagwarantować monadyjność NM. Aby to zobaczyć, potrzebujesz jednostki i wielomianu. skupię się na mult (Jednostka to unit_N unitM)

NMNM - l -> NNMM - mult_N mult_M -> NM

To nie gwarantuje, że MN jest monadą.

Ważne spostrzeżenie wchodzi jednak w grę, gdy masz rozwiązania prawa dystrybucyjnego]}
l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN
Tak więc LM, LN i MN są monadami. Powstaje pytanie, czy LMN jest monadą (albo przez

(MN)L - >L (MN) lub przez N (LM) - > (LM) N

Mamy wystarczającą strukturę, aby stworzyć te mapy. Jednak, jak zauważa Eugenia Cheng, potrzebujemy sześciokątnego warunku (który odpowiada prezentacji równania Yang-Baxtera), aby zagwarantować monadyczność obu konstrukcji. W rzeczywistości, przy stanie sześciokątnym, dwie różne monady pokrywają się.

 7
Author: user278559,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 00:07:07

Oto kod tworzący monadę poprzez pracę prawa dystrybucyjnego. Zauważ, że istnieją prawa dystrybucyjne od każdej monady do monady Maybe, Either, Writer i []. Z drugiej strony, nie znajdziesz takich (ogólnych) praw dystrybucyjnych w Reader i State. Do tego potrzebne będą transformatory monad.

 {-# LANGUAGE FlexibleInstances #-}
 
 module ComposeMonads where
 import Control.Monad
 import Control.Monad.Writer.Lazy
 
 newtype Compose m1 m2 a = Compose { run :: m1 (m2 a) }
 
 instance (Functor f1, Functor f2) => Functor (Compose f1 f2) where
   fmap f = Compose . fmap (fmap f) . run
 
 class (Monad m1, Monad m2) => DistributiveLaw m1 m2 where
   dist :: m2 (m1 a) -> m1 (m2 a)
 
 instance (Monad m1,Monad m2, DistributiveLaw m1 m2)
           => Applicative (Compose m1 m2) where
     pure = return
     (<*>) = ap
 
 instance (Monad m1, Monad m2, DistributiveLaw m1 m2)
           => Monad (Compose m1 m2) where
   return = Compose . return . return
   Compose m1m2a >>= g =
     Compose $ do m2a <- m1m2a -- in monad m1
                  m2m2b <- dist $ do a <- m2a  -- in monad m2
                                     let Compose m1m2b = g a
                                     return m1m2b
                                  -- do ... ::  m2 (m1 (m2 b))
                           -- dist ... :: m1 (m2 (m2 b))          
                  return $ join m2m2b -- in monad m2
 
 instance Monad m => DistributiveLaw m Maybe where
   dist Nothing = return Nothing
   dist (Just m) = fmap Just m
 
 instance Monad m => DistributiveLaw m (Either s) where
   dist (Left s) = return $ Left s
   dist (Right m) = fmap Right m
 
 instance Monad m => DistributiveLaw m [] where
   dist = sequence
 
 instance (Monad m, Monoid w) => DistributiveLaw m (Writer w) where
   dist m = let (m1,w) = runWriter m
            in do a <- m1
                  return $ writer (a,w)
 
 liftOuter :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
                    m1 a -> Compose m1 m2 a
 liftOuter = Compose . fmap return
 
 liftInner :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
                    m2 a -> Compose m1 m2 a
 liftInner = Compose . return
 
 
   
 
 0
Author: tillmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-14 16:44:43