Aplikatory komponują, monady nie
Aplikatory komponują, monady nie.
Co oznacza powyższe stwierdzenie? A kiedy jedno jest lepsze od drugiego?
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!
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."
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
.
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
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ę.
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
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