Jak wymawia się te funkcje w typeklasie Applicative:
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(To znaczy, gdyby nie byli operatorami, jak mogliby się nazywać?)
Na marginesie, gdybyś mógł zmienić nazwę pure
na coś bardziej przyjaznego dla nie-matematyków, jak byś to nazwał?
pure
może byćmakeApplicative
.pure
sugestię jako odpowiedź, a ja cię zagłosujęOdpowiedzi:
Myślę, że znajomość matematyki, czy nie, jest tutaj w dużej mierze nieistotna. Jak zapewne wiesz, Haskell pożycza kilka fragmentów terminologii z różnych dziedzin abstrakcyjnej matematyki, w szczególności z teorii kategorii , skąd mamy funktory i monady. Użycie tych terminów w Haskell różni się nieco od formalnych definicji matematycznych, ale i tak są one na tyle bliskie, że i tak są dobrymi terminami opisowymi.
Applicative
Klasa typ siedzi gdzieś pomiędzyFunctor
iMonad
tak można by oczekiwać, że mają podobną podstawę matematycznego. DokumentacjaControl.Applicative
modułu zaczyna się od:Hmm.
Monad
Myślę, że nie tak chwytliwe, jak .Wszystko to w zasadzie sprowadza się do tego,
Applicative
że nie pasuje do żadnej szczególnie interesującej koncepcji matematycznie, więc nie ma gotowych terminów, które opisują sposób, w jaki jest używany w Haskell. Więc na razie odłóż na bok matematykę.Jeśli chcemy wiedzieć, jak zadzwonić
(<*>)
, może pomóc wiedzieć, co to w zasadzie oznacza.Więc o co chodzi
Applicative
i dlaczego tak to nazywamy?Co
Applicative
wynosi w praktyce jest to sposób, aby podnieść dowolne funkcje wFunctor
. Rozważmy kombinacjęMaybe
(prawdopodobnie najprostszy nietrywialnyFunctor
) iBool
(podobnie jak najprostszy nietrywialny typ danych).Ta funkcja
fmap
pozwala nam podnieść sięnot
z pracyBool
do pracyMaybe Bool
. Ale co, jeśli chcemy podnieść(&&)
?Cóż, to nie jest to, co chcemy w ogóle ! W rzeczywistości jest to prawie bezużyteczne. Możemy starać się być mądry i podkraść innego
Bool
doMaybe
w tył ...... ale to nie jest dobre. Po pierwsze, jest źle. Po drugie, jest brzydki . Moglibyśmy próbować dalej, ale okazuje się, że nie ma sposobu, aby podnieść funkcję wielu argumentów do pracy na dowolnej
Functor
. Denerwujący!Z drugiej strony, możemy to zrobić z łatwością, jeśli użyliśmy
Maybe
„sMonad
instancji:Teraz to dużo kłopotów, żeby przetłumaczyć prostą funkcję - dlatego
Control.Monad
zapewnia funkcję, aby to zrobić automatycznieliftM2
. Dwójka w nazwie odnosi się do faktu, że działa na funkcjach o dokładnie dwóch argumentach; podobne funkcje istnieją dla funkcji 3, 4 i 5 argumentowych. Te funkcje są lepsze , ale nie doskonałe, a określanie liczby argumentów jest brzydkie i niezdarne.To prowadzi nas do artykułu, który wprowadził klasę typu Applicative . W nim autorzy dokonują zasadniczo dwóch obserwacji:
Functor
jest rzeczą bardzo naturalnąMonad
Normalna aplikacja funkcji jest napisana przez proste zestawienie terminów, więc aby uczynić "podniesioną aplikację" tak prostą i naturalną, jak to tylko możliwe, w artykule przedstawiono operatory wrostków, które zastępują aplikację, przeniesione do
Functor
i klasy typu, aby zapewnić to, co jest do tego potrzebne .Wszystko to prowadzi nas do następującego punktu:
(<*>)
po prostu reprezentuje aplikację funkcji - dlaczego więc wymawiać ją inaczej niż „operator zestawienia” z białymi znakami?Ale jeśli to nie jest zbyt satysfakcjonujące, możemy zauważyć, że
Control.Monad
moduł udostępnia również funkcję, która robi to samo dla monad:Gdzie
ap
jest oczywiście skrótem od „aplikuj”. Ponieważ każdyMonad
może byćApplicative
iap
potrzebuje tylko podzbioru funkcji obecnych w tym drugim, możemy być może powiedzieć, że gdyby(<*>)
nie był operatorem, należałoby go wywołaćap
.Możemy też podejść do rzeczy z innej strony. Operacja
Functor
podnoszenia nazywa się,fmap
ponieważ jest uogólnieniemmap
operacji na listach. Jak wyglądałaby funkcja na listach(<*>)
? Jest coap
działa na listach, ale samo w sobie nie jest to szczególnie przydatne.W rzeczywistości istnieje być może bardziej naturalna interpretacja list. Co przychodzi na myśl, gdy patrzysz na następujący podpis typu?
Jest coś tak kuszącego w pomyśle równoległego zestawiania list i przypisywania każdej funkcji z pierwszej do odpowiedniego elementu drugiej. Niestety dla naszego starego przyjaciela
Monad
, ta prosta operacja narusza prawa monady, jeśli listy mają różną długość. Ale to dobrzeApplicative
, w takim przypadku(<*>)
staje się sposobem na połączenie razem uogólnionej wersjizipWith
, więc może możemy sobie wyobrazić nazywanie tegofzipWith
?Ten pomysł na spakowanie faktycznie prowadzi nas do pełnego koła. Pamiętasz te matematyczne rzeczy wcześniej, o monoidalnych funktorach? Jak sama nazwa wskazuje, są to sposoby łączenia budowy monoidów i funktorów, z których oba są znanymi klasami typu Haskell:
Jak by wyglądały, gdybyś umieścił je razem w pudełku i trochę nim potrząsnął? Z
Functor
tego miejsca zachowamy ideę struktury niezależnej od jej parametru typu , a odMonoid
tego zachowamy ogólną postać funkcji:Nie chcemy zakładać, że istnieje sposób na stworzenie prawdziwie „pustego”
Functor
i nie możemy wyczarować wartości dowolnego typu, więc naprawimy typmfEmpty
asf ()
.Nie chcemy również wymuszać
mfAppend
konieczności posiadania spójnego parametru typu, więc teraz mamy to:Jaki jest typ wyniku
mfAppend
? Mamy dwa dowolne typy, o których nic nie wiemy, więc nie mamy wielu opcji. Najrozsądniej jest zachować jedno i drugie:W którym momencie
mfAppend
jest teraz wyraźnie uogólniona wersjazip
listy i możemyApplicative
łatwo zrekonstruować :To również pokazuje nam, że
pure
jest powiązany z elementem tożsamości aMonoid
, więc innymi dobrymi nazwami może być wszystko, co sugeruje wartość jednostkową, operację zerową lub tym podobne.To było długie, więc podsumowując:
(<*>)
jest po prostu zmodyfikowaną aplikacją funkcyjną, więc możesz ją odczytać jako „ap” lub „zastosować”, lub całkowicie usunąć ją w taki sam sposób, jak zwykłą aplikację funkcyjną.(<*>)
również z grubsza uogólniazipWith
listy, więc można to czytać jako „funktory zip z”, podobnie do czytaniafmap
jako „mapuj funktor z”.Pierwsza jest bliższa intencji
Applicative
klasy typu - jak sama nazwa wskazuje - więc to właśnie polecam.W rzeczywistości zachęcam do liberalnego używania i braku wymowy wszystkich operatorów podniesionych aplikacji :
(<$>)
, która przenosi funkcję jednoargumentową do plikuFunctor
(<*>)
, który łączy funkcję wieloparametrową za pomocą plikuApplicative
(=<<)
, który wiąże funkcję, która wprowadza aMonad
do istniejącego obliczeniaWszystkie trzy są w istocie zwykłymi aplikacjami funkcyjnymi, nieco doprawionymi.
źródło
Applicative
a funkcjonalny styl idiomatyczny, który promuje, nie daje wystarczającej miłości, więc nie mogłem oprzeć się szansie, aby trochę wychwalać jego zalety, aby wyjaśnić, jak (nie) wymawiam(<*>)
.Applicative
! Coś w rodzaju[| f a b c d |]
(jak sugerował oryginalny artykuł). Wtedy nie potrzebowalibyśmy<*>
kombinatora i odnosiłbyś się do takiego wyrażenia jako przykładu „zastosowania funkcji w kontekście funktorskim”Monad
. LubFunctor
lubMonoid
cokolwiek innego, co ma ugruntowany termin obejmujący mniej niż trzy przymiotniki. „Applicative” to po prostu mało inspirująca, aczkolwiek dość opisowa nazwa narzucona na coś, co raczej jej potrzebowało.Ponieważ nie mam ambicji ulepszania technicznej odpowiedzi CA McCann , zajmę się bardziej puszystą:
Jako alternatywę, zwłaszcza że nie ma końca nieustannym, pełnym lęku i zdrady wołaniu przeciwko
Monad
wersji, zwanej "return
", proponuję inną nazwę, która sugeruje jej funkcję w sposób, który może zaspokoić najbardziej imperatywny programista i najbardziej funkcjonalny z ... cóż, miejmy nadzieję, że każdy może narzekać na to samo:inject
.Weź wartość. „Wstrzyknąć” go do
Functor
,Applicative
,Monad
, lub co-ma-ty. Głosuję na „inject
” i zaakceptowałem tę wiadomość.źródło
inject
to doskonała nazwa i prawdopodobnie lepsza od mojej, chociaż na marginesie, słowo „inject” jest używane w - myślę - Smalltalk i Ruby dla jakiejś metody składania w lewo. Jednak nigdy nie rozumiałem tego wyboru imienia ...inject
w Ruby i Smalltalk jest używany, ponieważ jest to tak, jakbyś "wstrzykiwał" operator między każdy element na liście. Przynajmniej tak zawsze o tym myślałem.foldr
. (Zamieniasz(:)
i[]
, gdzie(:)
przyjmuje 2 argumenty i[]
jest stałą, stądfoldr (+) 0 (1:2:3:[])
↝1+2+3+0
.) PoBool
prostuif
-then
-else
(dwie stałe, wybierz jedną) iMaybe
nazywa sięmaybe
… Haskell nie ma jednej nazwy / funkcji do tego, ponieważ wszystkie mają różne typy (generalnie elim to po prostu rekurencja / indukcja)W skrócie:
<*>
możesz to nazwać stosowaniem . WięcMaybe f <*> Maybe a
może być wymawiane jako ApplyMaybe f
overMaybe a
.Możesz zmienić nazwę
pure
naof
, tak jak robi to wiele bibliotek JavaScript. W JS można utworzyćMaybe
zMaybe.of(a)
.Również wiki Haskell'a ma stronę na wymowie operatorów językowych tutaj
źródło
Źródło: Haskell Programming from First Principles , autor: Chris Allen i Julie Moronuki
źródło