2014-09-24 20 views
19

Rozumiem rozumowanie sygnatury typu <$>, ponieważ jest to tylko wersja infiksowa fmap, ale porównanie jej z sygnaturą typu >>= ma dla mnie dużo mniejszy sens.

Najpierw ustalmy, co przez to rozumiem.

(>>=) :: Monad m => m a -> (a -> m b) -> m b 
(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
(<$>) :: Functor f => (a -> b) -> f a -> f b 

Patrząc na podpisami typu możemy zobaczyć, że >>= przyjmuje wartość po lewej stronie, a funkcja po prawej stronie, co sprawia, że ​​wiele sensu, jeśli wziąć pod uwagę jego właściwość łańcuchowy: foo >>= bar >>= baz

Która Prowadzi mnie do zastanawiania się, dlaczego nie robisz tego też? nie można pisać foo <*> bar <*> baz, ponieważ wymagałoby to, aby wyjście foo <*> bar było funkcją, a nie wartością.

wiem, że <**> i =<< istnieć że zarówno odwrócić kolejność parametrów, pozwalając mi zrobić coś takiego:

Just 4 <**> pure (+3) <**> pure (*2) >>= (\x -> Just (x-3)) 

co mogło być pięknie zmniejszone do:

Just 4 <$$> (+3) <$$> (*2) >>= (\x -> Just (x-3)) 

Jeśli <$$> istniał, lub jeśli kolejność parametrów <$> i <*> została odwrócona.

Inną rzeczą, która sprawia, że ​​zastanawiam się, dlaczego istnieje różnica, jest to, że utrudniają początkującym przyzwyczajenie się i/lub zapamiętanie, czy to funkcja, czy wartość, która jest najważniejsza, bez konieczności sprawdzania.

Więc dlaczego jest tak, że w przypadkach <*> i <$> to fn op val ale z >>= to val op fn?

+4

ale często chcesz dokładnie to z 'Applicatives' : na przykład '(+) <$> Tylko 5 <*> Tylko 5 ' <- nie jest tym, że "Po prostu" piękne;) – Carsten

+2

'(>> =)' konstruuje łańcuchy "w stylu imperatywnym" (w przeciwnym razie zastanawialibyśmy się, dlaczego to nie jest w takiej samej kolejności jak '$'). '(<= <)' pozwala również na łańcuchowanie - "' (.) 'styl". '(<*>)' jest przydatne do "stosowania" funkcji wielu argumentów - dostarczając argumenty w kolejności deklaracji. –

+2

Prawdziwe pytanie brzmi, dlaczego '(>> =)' używa prawie odwrotnego porządku od wszystkiego w Haskell. Typ '(= <<) :: Monad m => (a -> mb) -> (ma -> mb)' wygląda bardziej jak wszystko inne w Haskell niż '' (>> =) = odwróć (= <<) '. –

Odpowiedz

18

Odpowiedź "dlaczego bierze parametry w tej kolejności" jest w zasadzie "ponieważ". Ktokolwiek zdefiniował te funkcje, uznał to za najlepszy sposób. (I pewnie to nie była ta sama osoba w każdym przypadku). Jednak podam kilka przykładów:

Załóżmy, że mamy monotażową monadę.Załóżmy również, że mamy zdefiniowane

data Foobar = Foobar X Y Z 

parseFoo :: Parser X 
parseBar :: Parser Y 
parseBaz :: Parser Z 

Wtedy możemy napisać

parseFoobar :: Parser Foobar 
parseFoobar = do 
    foo <- parseFoo 
    bar <- parseBar 
    baz <- parseBaz 
    return (Foobar foo bar baz) 

też wyraźnie,

parseFoobar = 
    parseFoo >>= \ foo -> 
    parseBar >>= \ bar -> 
    parseBaz >>= \ baz -> 
    return (Foobar foo bar baz) 

Teraz napisać, że aplikacyjny-style:

parseFoobar = return Foobar <*> parseFoo <*> parseBar <*> parseBaz 

lub alternatywnie

parseFoobar = Foobar <$> parseFoo <*> parseBar <*> parseBaz 

Jeśli założymy, że <**> = flip <*> istnieje (i ma prawidłowe skojarzenie), to mamy

parseFoobar = parseBaz <**> parseBar <**> parseFoo <**> return Foobar 

To po prostu wygląda dziwnie. Z funkcją na końcu i argumentami w odwrotnej kolejności? Dlaczego chciałbyś to napisać w ten sposób? (Zauważ, że wszelkie efekty są również w odwrotnej kolejności.)

W monadycznej wersji efekty występują od góry do dołu. W wersji aplikacyjnej efekty występują od lewej do prawej. Wydaje się to "naturalne".

+0

możesz dodać przykładowe typy do funkcji przykładowych? tylko dlatego, że łatwiej mi jest owijać głowę w to, co się dzieje. –

+0

Zmieniono trochę rzeczy w. – MathematicalOrchid

+0

Jak to działa pod względem funkcji łączenia łańcuchów? to co tutaj robisz to zastosowanie funkcji do 3 wartości zawijanych, ale 'Tylko 4 >> = foo >> = bar >> = baz', działa znacznie inaczej niż powiedz' (+) <$> Tylko 3 <*> Tylko 4' –

22

Nie pozwól monadom wejść tutaj. Pomyśl o aplikacji .

Porównaj:

(<$>) :: Functor f => (a -> b) -> f a -> f b 

i

($) :: (a -> b) -> a -> b 

Widać istnieje związek między regularnym stosowaniu i aplikacji pod funktora.

Ciekawostki: doszło proposals używać nawiasów przeciążać białymi (aplikacji), dzięki czemu możemy napisać:

(| f a1 .. an |) 

zamiast

pure f <*> a1 <*> .. <*> an 
+4

Nigdy tego nie widziałem, patrzyłem tylko na '<$>' pod względem 'fmap', teraz gdy przedstawiłeś go jako wariant' $ 'ma on dużo więcej sensu –

+1

Myślę, że to powinno być akceptowane odpowiedź! –

Powiązane problemy