Jak się nazywa <*> i co robi? [zamknięte]

zamknięte . To pytanie jest oparte na opinii . Obecnie nie przyjmuje odpowiedzi. Zamknięty 2 lata temu . zamknięte . To pytanie i jego odpowiedzi są zamknięte , ponieważ pytanie jest off-topic, ale ma znaczenie historyczne. Obecnie nie przyjmuje nowych odpowiedzi ani interakcji.

Jak działają te funkcje w typeklasie aplikacji?

(<*>) :: 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 nie-matematykom, jak byś to nazwał?

 112
Author: TylerH, 2010-07-13

4 answers

Przepraszam, nie znam się na matematyce, więc jestem ciekaw, jak wymawiać funkcje w typeklasie aplikacyjnym [83]}

Znajomość matematyki, czy nie, jest tu w dużej mierze nieistotna, myślę. Jak zapewne wiesz, Haskell zapożycza kilka fragmentów terminologii z różnych dziedzin abstrakcyjnej matematyki, przede wszystkim teoria kategorii , skąd otrzymujemy funktory i monady. Użycie tych terminów w Haskell odbiega nieco od formalnych definicji matematycznych, ale zwykle są na tyle blisko, że i tak są dobrymi określeniami opisowymi.

Klasa typu Applicative znajduje się gdzieś pomiędzy Functor a Monad, więc można oczekiwać, że będzie miała podobną matematyczną podstawę. Dokumentacja modułu Control.Applicative zaczyna się od:

Ten moduł opisuje strukturę pośrednią między funktorem a monadą: dostarcza czystych wyrażeń i sekwencjonowania, ale nie wiąże. (Technicznie, silny monoidalny funkcjonariusz.)

Hmm.

class (Functor f) => StrongLaxMonoidalFunctor f where
    . . .

Nie tak chwytliwe jak Monad, myślę.

Wszystko to sprowadza się do tego, że Applicative nie odpowiada żadnemu pojęciu, które jest szczególnie interesujące {101]} matematycznie, więc nie ma gotowych terminów, które odzwierciedlają sposób, w jaki są używane w Haskell. Więc odstaw matematykę na bok.


Jeśli chcemy wiedzieć, jak się nazywać {[18] } może pomóc wiedzieć, co to w zasadzie znaczy.

Więc o co chodzi z Applicative i dlaczego robimy tak to nazywamy?

Co Applicative jest w praktyce sposobem na podniesienie arbitralnych funkcji do Functor. Rozważmy kombinację Maybe (prawdopodobnie najprostszy nietrywialny Functor) i Bool (podobnie najprostszy nietrywialny typ danych).

maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not

Funkcja fmap pozwala nam podnieść not z pracy nad Bool do pracy nad Maybe Bool. Ale co jeśli chcemy podnieść (&&)?

maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)

Cóż, to wcale nie tego chcemy! Właściwie, to jest całkiem bezużyteczne. Możemy spróbować być sprytni i przemycić kolejny Bool do Maybe przez plecy...

maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)

...ale to nie jest dobre. Po pierwsze, to jest złe. Jeszcze jedno, to jest brzydkie . Możemy próbować dalej, ale okazuje się, że nie ma sposobu, aby podnieść funkcję wielu argumentów do pracy na arbitralnej Functor. Irytujące!

Z drugiej strony, możemy to zrobić łatwo, jeśli użyjemy Maybe ' s Monad instancja:

maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
                  y' <- y
                  return (x' && y')

Tłumaczenie prostej funkcji jest kłopotliwe, dlatego Control.Monad zapewnia funkcję, która robi to automatycznie, {36]}. 2 w swojej nazwie odnosi się do faktu, że działa na funkcjach dokładnie dwóch argumentów; podobne funkcje istnieją dla funkcji 3, 4 i 5 argumentów. Funkcje te są lepsze , ale nie doskonałe, a podanie liczby argumentów jest brzydkie i niezdarne.

Co prowadzi nas do papieru, który wprowadzono klasę typu aplikacyjnego . W nim autorzy poczynili zasadniczo dwie uwagi: [83]}

  • przeniesienie funkcji wielowątkowych do Functor jest bardzo naturalną rzeczą do zrobienia
  • To nie wymaga pełnych możliwości]}

Aplikacja funkcji normalnej jest zapisywana za pomocą prostego zestawienia terminów, więc aby "aplikacja podnoszona" była jak najprostsza i naturalna, w artykule wprowadzono operatory infiksowe do wniosek, podnoszony do Functor, i klasy typu, aby zapewnić to, co jest potrzebne do tego.

Wszystko sprowadza nas do następującego punktu: (<*>) po prostu reprezentuje aplikację funkcyjną-więc po co ją wymawiać inaczej niż robi się to za pomocą "operatora zestawiania"?

Ale jeśli nie jest to zbyt satysfakcjonujące, możemy zauważyć, że moduł Control.Monad zapewnia również funkcję, która robi to samo dla monad: {83]}

ap :: (Monad m) => m (a -> b) -> m a -> m b

Gdzie ap jest, z oczywiście, skrót od "apply". Ponieważ dowolny Monad Może być Applicative, a ap potrzebuje tylko podzbioru funkcji obecnych w tym ostatnim, możemy być może powiedzieć, że gdyby (<*>) nie był operatorem, powinien być nazywany ap.


Możemy również podchodzić do rzeczy z innej strony. Operacja liftingu Functor nazywa się fmap, ponieważ jest uogólnieniem operacji map na listach. Jaka funkcja na listach będzie działać (<*>)? Jest to, co ap robi na listach, oczywiście, ale samo to nie jest 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?

listApply :: [a -> b] -> [a] -> [b]

Jest coś tak kuszącego w idei równoległego ustawiania list, stosując każdą funkcję w pierwszej do odpowiadającego jej elementu drugiej. Niestety dla naszego starego przyjaciela Monad, ta prosta operacja narusza prawa monad jeśli listy są różnej długości. Ale to sprawia, że grzywna Applicative, w którym to przypadku (<*>) staje się sposobem naciągania uogólnionej wersji zipWith, więc może wyobrażamy sobie nazywanie jej fzipWith?


Ten pomysł zapinania zapięcia zapina nas w koło. Pamiętasz te matematyczne rzeczy o funkcjach monoidalnych? Jak sama nazwa wskazuje, są to sposoby łączenia struktury monoidów i funktorów, z których oba są znane typu Haskell klasy:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a

Jak by to wyglądało, gdybyś włożył je do pudełka razem i potrząsnął nim trochę? Z Functor zachowamy ideę struktury niezależną od jej parametru typu, a z Monoid zachowamy ogólną formę funkcji:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ?
    mfAppend :: f ? -> f ? -> f ?

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 Typ mfEmpty jako f ().

My też nie chcemy aby wymusić mfAppend potrzebę spójnego parametru typu, więc teraz mamy to:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f ?

Jaki jest typ wyniku dla mfAppend? Mamy dwa dowolne typy, o których nic nie wiemy, więc nie mamy wielu opcji. Najrozsądniej jest po prostu zachować oba:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f (a, b)

W tym momencie mfAppend jest teraz wyraźnie uogólnioną wersją zip na listach i możemy łatwo zrekonstruowaćApplicative: {]}

mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)

To również pokazuje nam, że pure jest związane z elementem tożsamości Monoid, więc innymi dobrymi nazwami może być cokolwiek, co sugeruje Wartość jednostki, operację null lub coś takiego.


To było długie, więc podsumowując:

  • (<*>) jest tylko zmodyfikowaną aplikacją funkcyjną, więc możesz albo odczytać ją jako " ap " lub "apply", albo ominąć ją całkowicie tak, jak normalnie działa aplikacja.
  • (<*>) również z grubsza uogólnia zipWith na listy, więc można go odczytać jako "funkcje zip z", podobnie jak czytanie fmap jako " mapa funkcjonariusz z".

Pierwsza jest bliższa intencji Applicative klasy typu-jak sama nazwa wskazuje-więc to polecam.

W rzeczywistości zachęcam do liberalnego użycia i nie-wymowy wszystkich operatorów podnoszonych aplikacji: {]}
  • (<$>), który podnosi funkcję jednowątkową do Functor
  • (<*>), który łączy funkcję wielowątkową za pomocą Applicative
  • (=<<), która wiąże funkcję, która zapisuje Monad na istniejące obliczenia

Wszystkie trzy są, w sercu, po prostu regularne stosowanie Funkcji, trochę doprawione.

 250
Author: C. A. McCann,
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
2010-07-14 01:55:47

Ponieważ nie mam ambicji poprawy na odpowiedź techniczna C. A. McCann, zajmę się tym bardziej puszystym:

Gdybyś mógł zmienić nazwę pure na coś bardziej przyjaznego dla podunksów jak ja, jak byś to nazwał?

Jako alternatywę, tym bardziej, że nie ma końca ciągła, pełna gniewu i zdrady Wersja Monad, zwana " return", proponuję inną nazwę, która sugeruje jej funkcję w sposób, który może zaspokoić najbardziej imperatyw programistów imperatywnych, a najbardziej funkcjonalny z..cóż, mam nadzieję, że każdy może narzekać na to samo: inject.

Weź wartość. "Inject" it into the Functor, Applicative, Monad, albo co-masz-Ciebie. Głosuję na "inject " i zatwierdziłem tę wiadomość.

 22
Author: BMeph,
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
2017-05-23 12:34:33

W skrócie:

  • <*> można go nazwać apply . Więc {[1] } można wymawiać jako Zastosuj Maybe f nad Maybe a.

  • Możesz zmienić nazwę pure na of, tak jak robi to wiele bibliotek JavaScript. W JS możesz utworzyć Maybe z Maybe.of(a).

Na wiki Haskella znajduje się również strona o wymowie operatorów językowych tutaj
 7
Author: Marcelo Lazaroni,
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
2017-04-08 10:39:57
(<*>) -- Tie Fighter
(*>)  -- Right Tie
(<*)  -- Left Tie
pure  -- also called "return"

Źródło: Programowanie Haskella z pierwszych zasad , autorstwa Chrisa Allena i Julie Moronuki

 3
Author: dmvianna,
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
2016-05-31 02:14:13