2011-07-04 9 views
64

Jestem nowy w Haskell, i czytam o funktorach i funktorach użytkowych. Ok, rozumiem funktory i jak mogę z nich korzystać, ale nie rozumiem, dlaczego funktory są użyteczne i jak mogę ich używać w Haskell. Czy możesz mi wytłumaczyć prostym przykładem, dlaczego potrzebuję funktory aplikacyjne?Dlaczego powinienem używać funktory aplikacyjne w programowaniu funkcjonalnym?

+1

Możliwy duplikat: [W jaki sposób używać Control.Applicative, aby napisać czystszy Haskell?] (Http://stackoverflow.com/questions/2104446/how-do-you-use-control-applicative-to-write-cleaner -haskell) –

+0

To jest tylko link, który mogę podać, ale [tutaj] (http://www.haskell.org/haskellwiki/Applicative_functor) jest ładnym opisem Applicative Funktorów z przykładami. – Hartmut

Odpowiedz

48

Applicative functors są konstrukcją, która zapewnia środek między funktorami i monads, a zatem są bardziej rozpowszechnione niż monady, a jednocześnie bardziej użyteczne niż funktory. Zwykle możesz po prostu odwzorować funkcję na funktora. Funkcjonalne funktory pozwalają ci wziąć "normalną" funkcję (przyjmując nie-funktorialne argumenty), używając jej do działania na kilku wartościach, które są w kontekstach funktora. W efekcie daje to skuteczne programowanie bez monad.

Ładne, samodzielne wyjaśnienie obfitujące w przykłady można znaleźć here. Możesz również przeczytać a practical parsing example opracowany przez Bryana O'Sullivana, który nie wymaga wcześniejszej wiedzy.

+1

Istnieją pomocne linki, ale nie sądzę, że mogą zastąpić krótki prosty przykład skupiony na odpowiedzi na to pytanie, ze wszystkimi nieistotnymi szczegółami usuniętymi w jak największym stopniu. –

12

Conor McBride i Ross Paterson w stylu Functional Pearl ma kilka dobrych przykładów. Odpowiada także za popularyzację stylu. Używają terminu "idiom" dla "funktora aplikacyjnego", ale poza tym jest całkiem zrozumiały.

6

Jeden dobry przykład: analiza aplikacyjna.

See [Real World Haskell] CH16 http://book.realworldhaskell.org/read/using-parsec.html#id652517

Jest to kod parser z zrób notacji:

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

Korzystanie funktor uczynić go znacznie krótszy:

-- 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 niektórych powtarzających się kodów. wtedy możesz po prostu użyć mniej słów, aby dokładnie opowiedzieć dokładną historię.

+4

Masz dziwny pomysł "znacznie krótszy" - wersja aplikacyjna jest dłuższa o 7 znaków! –

+0

@ Daniel Wagner: -_- ||, Oh my .. masz dobry wgląd w kod, muszę się przyznać. Właściwie oznacza to "pionowo krótszy" :) – Nybble

+0

Cóż, można go napisać krótszy przy użyciu typowych funkcji z biblioteki Control.Monad: 'char '%' >> liftM (toEnum.starter head. ReadHex) (replicateM 2 hexDigit)' . – HaskellElephant

29

Funkcjonalne funktory są użyteczne, gdy potrzebujesz sekwencjonowania działań, ale nie musisz podawać żadnych pośrednich wyników. Są więc słabsze od monad, ale silniejsze od funktorów (nie mają wyraźnego operatora powiązania, ale pozwalają na uruchamianie dowolnych funkcji wewnątrz funktora).

Kiedy są przydatne? Typowym przykładem jest parsowanie, w którym należy uruchomić szereg działań, które po części odczytują strukturę danych, a następnie sklejają wszystkie wyniki razem. To jest jak z ogólną formę złożenie funkcji:

f a b c d 

gdzie można myśleć a, b i tak dalej jako samowoli uruchomić, a f jako funktor zastosować do wyniku.

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

Lubię myśleć o nich jako o przeciążonych "białych literach". Lub, że regularne funkcje Haskella znajdują się w funktorze aplikowania tożsamości.

Patrz „Applicative Programming with Effects

8

Trudno wymyślić przykłady gdzie potrzebę aplikacyjne funktory. Rozumiem, dlaczego pośredni programista Haskella zadawałby sobie to pytanie, ponieważ większość tekstów wprowadzających przedstawia instancje wyprowadzone z Monad z wykorzystaniem Funkcjonalnych Funkcji tylko jako wygodny interfejs.

Kluczowy wgląd, jak wspomniano tutaj i w większości wprowadzeń do tematu, polega na tym, że Funkcjonalne są między funktorami i monadami (nawet między funktorami a strzałkami). Wszystkie Monady są Funkcjonalnymi Funkcjonariuszami, ale nie wszystkie Funktory są Stosowane.

Tak więc czasami możemy użyć kombinatorów aplikacyjnych dla czegoś, do czego nie możemy użyć monadycznych kombinatorów. Jedną z takich rzeczy jest ZipList (zobacz także this SO question for some details), która jest po prostu listą zawijającą, aby mieć inną instancję aplikacji niż instancję pochodzącą z instancji listy Monad. Aplikacyjnych dokumentacja wykorzystuje następujący wiersz dać intuicyjne pojęcie o tym, co jest dla ZipList:

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

Jak wskazano here, możliwe jest, aby dziwnych przypadków monady, że prawie pracy dla ZipList.

Istnieją inne Funkcjonatory Applicative, które nie są Monadami (patrz: this Pytanie SO) i są łatwe do wymyślenia. Posiadanie alternatywnego interfejsu dla Monad jest ładne i wszystkie, ale czasami tworzenie Monady jest nieefektywne, skomplikowane, a nawet niemożliwe, i to jest, gdy potrzebujesz Applicative Funktorów.


disclaimer: Making aplikacyjnych funktory może również być nieefektywne, skomplikowane i niemożliwe, w razie wątpliwości, należy skontaktować się z lokalnym kategorii teoretyka za prawidłową eksploatację aplikacyjnych funktorów.

3

Chciałbym również zasugerować, aby przyjrzeć this

w końcu tego artykułu nie jest przykład

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

który ilustruje kilka cech aplikacyjnej stylu programowania.

6

Z mojego doświadczenia wynika, aplikacyjnych funktory są idealne do następujących powodów:

Niektóre rodzaje struktur danych przyznać potężne rodzaje kompozycji, ale nie może naprawdę być wykonane monady. W rzeczywistości większość abstrakcji w funkcjonalnym programowaniu reaktywnym należy do tej kategorii. Chociaż możemy technicznie być w stanie wykonać np. Behavior (alias Signal) Monada, zwykle nie można zrobić wydajnie. Funkcjonalne funktory pozwalają nam wciąż mieć potężne kompozycje bez utraty wydajności (wprawdzie trochę trudniej jest użyć aplikatora niż monady, tylko dlatego, że nie masz wystarczająco dużo struktury do pracy).

Brak zależności od danych w funktorach aplikacyjnych umożliwia np. przetestować działanie, szukając wszystkich efektów, jakie może wytworzyć, nie mając dostępnych danych.Więc można sobie wyobrazić „formularza internetowego” aplikacyjnych, używane tak:

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

i można napisać silnik, który będzie przechodzić znaleźć wszystkie pola używane i wyświetlać je w formie, a następnie, gdy pojawi się dane Z powrotem uruchom go ponownie, aby uzyskać skonstruowany User. To nie może być zrobione ze zwykłego funktora (ponieważ łączy dwie formy do jednego), ani monady, ponieważ z 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 drugim polu nie może być znanym bez uprzedniej odpowiedzi od pierwszego. Jestem prawie pewien, że istnieje biblioteka, która implementuje ten pomysł form - kilka razy wykonałem swój własny projekt.

Inną fajną rzeczą związaną z funktorami aplikacyjnymi jest to, że tworzą. Dokładniej, funktor skład:

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

jest aplikacyjnych gdy f i g są. Tego samego nie można powiedzieć o monadach, które stworzyły całą monadyczną historię transformatora, która jest skomplikowana w nieprzyjemny sposób. Zastosowania są super czyste w ten sposób, a to oznacza, że ​​można zbudować strukturę potrzebnego typu, koncentrując się na małych składanych komponentach.

Niedawno w GHC pojawiło się rozszerzenie ApplicativeDo, które pozwala na stosowanie notacji z aplikacjami, zmniejszając część złożoności notacyjnej, o ile nie robisz żadnych monad.

Powiązane problemy