Dlaczego powinienem używać funktorów aplikacyjnych w programowaniu funkcyjnym?

Jestem nowy w Haskell i czytam o funktorach i funkcjach aplikacyjnych. Ok, rozumiem funktory i jak mogę ich używać, ale nie rozumiem dlaczego applicative funktory są użyteczne i jak mogę ich używać w Haskell. Czy możesz mi wyjaśnić prostym przykładem, dlaczego potrzebuję funktorów aplikacyjnych?

Author: 0xAX, 2011-07-04

7 answers

Funktory Applicative są konstrukcją, która zapewnia punkt środkowy pomiędzy funktorami i monadami , a zatem są bardziej rozpowszechnione niż monady, podczas gdy są bardziej użyteczne niż funktory. Zwykle można po prostu odwzorować funkcję nad funktorem. Funktory aplikacyjne pozwalają przyjąć "normalną" funkcję (przyjmując argumenty niefunkcjonalne), używając jej do operowania na kilku wartościach znajdujących się w kontekstach funktora. W rezultacie daje to skuteczne programowanie bez monad.

A ładne, samodzielne Wyjaśnienie z przykładami można znaleźć tutaj . Możesz również przeczytać praktyczny przykład parsowania opracowany przez Bryana O ' Sullivana, który nie wymaga wcześniejszej wiedzy.

 56
Author: Francois G,
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-01-17 22:05:44

Funktory aplikacyjne są przydatne, gdy potrzebujesz sekwencjonowania akcji, ale nie musisz wymieniać żadnych pośrednich wyników. Są więc słabsze od monad, ale silniejsze od funktorów (nie mają wyraźnego operatora bind, ale pozwalają na uruchamianie dowolnych funkcji wewnątrz funktora).

Kiedy są przydatne? Częstym przykładem jest parsowanie, w którym należy uruchomić szereg działań, które odczytują części struktury danych w kolejności, a następnie sklejają wszystkie wyniki razem. To jest jak ogólna forma składu funkcji:
f a b c d

Gdzie można myśleć o a, b i tak dalej jako dowolne działania do uruchomienia i f jako funktor do zastosowania do wyniku.

f <$> a <*> b <*> c <*> d

Lubię myśleć o nich jako o przeciążonych "białych znakach". Lub, że regularne funkcje Haskella są w funkcji aplikacyjnej tożsamości.

Patrz "Programowanie aplikacji z efektami"

 34
Author: Don Stewart,
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-10-17 15:02:39
 12
Author: John L,
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-07-04 13:15:46

Trudno wymyślić przykłady, w których Potrzebujesz funktorów aplikacyjnych. Mogę zrozumieć, dlaczego pośredni programista Haskell zadałby im to pytanie, ponieważ większość tekstów wprowadzających przedstawia instancje pochodzące z Monad za pomocą funkcji aplikacyjnych tylko jako wygodny interfejs.

Kluczowe spostrzeżenie, jak wspomniano zarówno tutaj, jak i w większości wstępów do tematu, polega na tym, że funktory aplikacyjne znajdują się między Funktorami a monadami (nawet między Funktorami a strzałkami). Wszystkie monady są funkcjami aplikacyjnymi, ale nie wszystkie są funkcjami aplikacyjnymi.

Więc koniecznie, czasami możemy użyć kombinatorów aplikacyjnych do czegoś, do czego nie możemy użyć kombinatorów monadycznych. Jedną z takich rzeczy jest ZipList (Zobacz także this SO question for some details), który jest tylko opakowaniem wokół list, aby mieć inną instancję aplikacyjną niż ta pochodząca z instancji monad list. Dokumentacja aplikacyjna wykorzystuje poniższy wiersz daje intuicyjne pojęcie, do czego służy ZipList:

f <$> ZipList xs1 <*> ... <*> ZipList xsn = ZipList (zipWithn f xs1 ... xsn)

Jak wspomniano tutaj , możliwe jest tworzenie dziwacznych instancji Monad, które prawie działają na ZipList.

Istnieją inne funktory aplikacyjne, które nie są monadami (patrz to więc pytanie) i są łatwe do wymyślenia. Posiadanie alternatywnego interfejsu dla Monad jest miłe i w ogóle, ale czasami tworzenie Monad jest nieefektywne, skomplikowane lub nawet niemożliwe, i to jest, gdy potrzebne funktory aplikacyjne.


zastrzeżenie: tworzenie funkcji aplikacyjnych może być również nieefektywne, skomplikowane i niemożliwe, w razie wątpliwości skonsultuj się z lokalnym teoretykiem kategorii w celu poprawnego użycia funkcji aplikacyjnych.

 8
Author: HaskellElephant,
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 11:47:19

Z mojego doświadczenia wynika, że funktory aplikacyjne są świetne z następujących powodów:]}

Niektóre rodzaje struktur danych dopuszczają potężne typy kompozycji, ale tak naprawdę nie mogą być wykonane z monad. W rzeczywistości większość abstrakcji w funkcyjnym programowaniu reaktywnym należy do tej kategorii. Chociaż technicznie możemy zrobić np. Behavior (aka Signal) monadę, zazwyczaj nie można tego zrobić efektywnie. Funktory aplikacyjne pozwalają nam nadal mieć potężne kompozycje bez rezygnacja z wydajności (co prawda, czasem użycie aplikatora jest nieco trudniejsze niż monada, tylko dlatego, że nie masz aż takiej struktury do pracy).

Brak zależności od danych w funkcji aplikacyjnej pozwala na np. przemierzanie akcji w poszukiwaniu wszystkich efektów, które może ona wywołać bez posiadania dostępnych danych. Można więc sobie wyobrazić aplikację "web form", używaną w ten sposób:

userData = User <$> field "Name" <*> field "Address"

I mógłbyś napisać silnik, który przemierzyłby by znaleźć wszystkie używane pola i wyświetlają je w formularzu, a następnie po odzyskaniu danych uruchom je ponownie, aby uzyskać constructed User. Nie można tego zrobić za pomocą zwykłego functora (ponieważ łączy on dwie formy w jedną), ani monady, ponieważ za pomocą monady można wyrazić: {]}

userData = do
    name <- field "Name"
    address <- field $ name ++ "'s address"
    return (User name address)

Które nie mogą być renderowane, ponieważ nazwa drugiego pola nie może być znana bez odpowiedzi z pierwszego. Jestem pewien, że istnieje biblioteka, która implementuje ten pomysł formularzy. posiadaj kilka razy na ten i tamten projekt.

Inną miłą rzeczą w funkcjach aplikacyjnych jest to, że tworzą . Dokładniej, funktor składu:

newtype Compose f g x = Compose (f (g x))

Ma zastosowanie zawsze, gdy f i g są. To samo nie da się powiedzieć o monadach, które stworzyły całą historię monad transformer, która jest skomplikowana w jakiś nieprzyjemny sposób. Aplikacje są bardzo czyste w ten sposób, a to oznacza, że możesz zbudować strukturę typu, którego potrzebujesz, koncentrując się na małe komponenty kompozytowe.

Ostatnio w GHC pojawiło się rozszerzenie ApplicativeDo, które pozwala używać notacji do z aplikacjami, łagodząc część złożoności notacji, o ile nie robisz żadnych monady rzeczy.

 7
Author: luqui,
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-12-27 02:20:03

Jeden dobry przykład: parsowanie aplikacyjne.

Zobacz[real world haskell] ch16 http://book.realworldhaskell.org/read/using-parsec.html#id652517

Jest to kod parsera z notacją do:

-- file: ch16/FormApp.hs
p_hex :: CharParser () Char
p_hex = do
  char '%'
  a <- hexDigit
  b <- hexDigit
  let ((d, _):_) = readHex [a,b]
  return . toEnum $ d

Użycie functora sprawia, że jest to znacznie krótsze :

-- file: ch16/FormApp.hs
a_hex = hexify <$> (char '%' *> hexDigit) <*> hexDigit
    where hexify a b = toEnum . fst . head . readHex $ [a,b]

"podnoszenie" może ukryć podstawowe szczegóły powtarzającego się kodu. wtedy możesz po prostu użyć mniej słów, aby opowiedzieć dokładną i precyzyjną historię.

 6
Author: wuxb,
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-07-04 16:24:21

Proponuję również rzucić okiem na to

Na końcu artykułu jest przykład

import Control.Applicative
hasCommentA blogComments =
BlogComment <$> lookup "title" blogComments
            <*> lookup "user" blogComments
            <*> lookup "comment" blogComments

Który ilustruje kilka cech stylu programowania aplikacji.

 3
Author: Vlad Patryshev,
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
2012-02-29 06:50:12