Skład funkcji Haskella (.) I idiomy aplikacji funkcji ($): prawidłowe użycie

130

Czytałem Real World Haskell i zbliżam się do końca, ale kwestia stylu mnie dręczy z operatorami (.)i ($).

Kiedy piszesz funkcję będącą kompozycją innych funkcji, piszesz ją tak:

f = g . h

Ale kiedy zastosujesz coś na końcu tych funkcji, piszę to w ten sposób:

k = a $ b $ c $ value

Ale książka napisałaby to tak:

k = a . b . c $ value

Dla mnie wyglądają na równoważne funkcjonalnie, robią dokładnie to samo w moich oczach. Jednak im więcej patrzę, tym częściej widzę ludzi piszących swoje funkcje w sposób, w jaki robi to książka: (.)najpierw komponuj, a dopiero na końcu użyj, ($)aby dodać wartość, aby ocenić partię (nikt nie robi tego z wieloma kompozycjami dolarowymi) .

Czy jest jakiś powód, by używać książek w sposób znacznie lepszy niż używanie wszystkich ($)symboli? Czy jest tu jakaś najlepsza praktyka, której nie rozumiem? A może jest to zbędne i nie powinienem się tym w ogóle martwić?

Robert Massaioli
źródło
4
Zauważ, że drugi przykład można zrobić jakok = a $ b $ c value
Thomas Eding
1
Tak, może, jak wspomniał Zifre poniżej, ale postanowiłem to tam zostawić, ponieważ nic to nie szkodzi i sprawia, że ​​jego (a teraz twoje) komentarze mają sens. Ale dziękuję. +1 :)
Robert Massaioli
2
Innym powszechnym podejściem jest to a . b $ c value, że nie podoba mi się tak bardzo jak trzeci przykład, ale oszczędza kilka znaków.
John L

Odpowiedzi:

154

Chyba mogę odpowiedzieć na to autorytetem.

Czy istnieje powód, aby używać książek w sposób znacznie lepszy niż używanie wszystkich symboli ($)?

Nie ma specjalnego powodu. Bryan i ja wolimy redukować szumy liniowe. .jest cichszy niż $. W rezultacie książka używa f . g . h $ xskładni.

Don Stewart
źródło
3
Cóż, nie mogę liczyć na lepszą odpowiedź niż ta; od samego jednego z autorów. :) I to ma sens, na stronie wygląda znacznie ciszej, gdy czytam. Oznaczając to jako odpowiedź, ponieważ bezpośrednio odpowiada na pytanie. Dziękuję za odpowiedź; i właściwie książka.
Robert Massaioli
38
Nie mogę nie zgodzić się z autorem, ale myślę, że w modelu mentalnym istnieje inny, bardziej widoczny powód, aby coś tworzyć, a nie używać . Użytkownicy Haskell f.g.hwolą raczej myśleć o nowym, sprytnym stworzeniu f(g(h())). Teraz wywołują nową, aczkolwiek anonimową funkcję, którą stworzyli, zamiast po prostu łączyć w łańcuch duży słownik gotowych wywołań funkcji, takich jak użytkownik PHP.
Evan Carroll
1
To ciekawe stwierdzenie: „zmniejsz szum liniowy” i „ciszej niż”. Nigdy nie myślałem o językach programowania w tej terminologii, ale ma to sens.
Rabarberski
54

W istocie są równoważne: pamiętaj, że $operator zasadniczo nic nie robi. f $ xocenia do f x. Celem $jest jego zachowanie związane ze stałością: prawostronna asocjacja i minimalny priorytet. Usunięcie $i użycie nawiasów do grupowania zamiast pierwszeństwa wrostków, fragmenty kodu wyglądają następująco:

k = a (b (c (value)))

i

k = (a . b . c) value

Powodem, dla którego preferujemy .wersję nad $wersją, jest ten sam powód, dla którego preferujemy obie wersje powyżej bardzo ujętej w nawiasy wersji: atrakcyjność estetyczna.

Chociaż niektórzy mogą się zastanawiać, czy użycie operatorów wrostków zamiast nawiasów jest oparte na jakiejś podświadomej chęci uniknięcia jakiegokolwiek możliwego podobieństwa do Lispa (tylko żartuję ... myślę?).

CA McCann
źródło
39

Dodam, że w f . g $ x, f . gjest znaczącą jednostką składniową.

Tymczasem w f $ g $ x, f $ gnie jest znacząca jednostka. Łańcuch $jest zapewne bardziej konieczne - najpierw uzyskać wynik gz x, następnie zrobić fz nim, a następnie zrobić fooz nim, potem itp

Tymczasem łańcuch .jest prawdopodobnie bardziej deklaratywny iw pewnym sensie bliższy widokowi skoncentrowanemu na przepływie danych - skomponuj serię funkcji i ostatecznie zastosuj je do czegoś.

sclv
źródło
2
Uczę się języka Haskell (nie zakodowałem niczego sensownego), ale lubię myśleć o $ jako o operatorze „potoku”. To bardzo przypomina terminal | potok (z wyjątkiem przepływu danych od prawej do lewej), ale podobnie jak procesy terminalowe, każda jednostka jest wyraźnie niezależna.
LostSalad
1
$Operator zachowuje się podobnie do <|operatora w F #. Uważam, że oba są zdefiniowane tak samo, w rzeczywistości - w F # (<|) a b = a bi Haskell a $ b = a b, które są zasadniczo równoważne.
Tom Galvin,
@Quackmatic Dość pewne, że jest (<|) b a = a bw języku F #, ponieważ jest używany tak [1; 2; 3; 4; 5] <| List.map ((+) 1), jakby służył pamięci.
BalinKingOfMoria Przywróć CM
@BalinKingOfMoria <|Operator przekazuje wartość do funkcji, zamiast przekazywać funkcję do wartości - Twój przykładowy kod będzie działał z |>operatorem.
Tom Galvin
@Quackmatic Oh. Nie wiedziałem, że istnieje wiele wersji (nie używałem zbyt wiele F #).
BalinKingOfMoria Przywróć CM
19

Myślę, że dla mnie odpowiedzią jest (a) porządek, jak powiedział Don ; i (b) stwierdzam, że kiedy edytuję kod, moja funkcja może skończyć się w stylu bez punktów, a wtedy wszystko, co muszę zrobić, to usunąć ostatnią, $zamiast cofać się i zmieniać wszystko. Niewątpliwie drobna uwaga, ale drobiazg.

Antal Spector-Zabusky
źródło
Tak, to dobry powód, żeby tak to napisać, jeśli chcesz zakończyć kompozycją funkcji, jest to szybsze. :) Brawo za oszczędność naciśnięć klawiszy, ale co ważniejsze, czasu. +1
Robert Massaioli
13

Jest interesująca dyskusja na ten temat w tym wątku haskell-cafe . Podobno istnieje mniejszość, która posiada punkt widzenia, że prawo asocjatywność o $to „po prostu źle” , a następnie wybierając f . g . h $ xna f $ g $ h $ xto jeden sposób side-stepping problem.

Travis Brown
źródło
Hej, znalazłeś całą dyskusję na ten temat. To jest niesamowite, teraz czytam. +1
Robert Massaioli
2
Podobne argumenty tutaj: ghc.haskell.org/trac/haskell-prime/wiki/...
Enrique
Zauważ, że $!ma również „po prostu złe” prawidłowe skojarzenie - dlaczego znak $! operator prawostronny? .
imz - Ivan Zakharyaschev 07.07.15
3

To tylko kwestia stylu. Jednak sposób, w jaki robi to książka, ma dla mnie większy sens. Składa wszystkie funkcje, a następnie stosuje je do wartości.

Twoja metoda po prostu wygląda dziwnie, a ostatnia $jest niepotrzebna.

Jednak to naprawdę nie ma znaczenia. W Haskell jest zwykle wiele, wiele poprawnych sposobów na zrobienie tego samego.

Zifre
źródło
3
Nie zauważyłem, że ostatni $ był niepotrzebny, ale powinienem był. Dzięki i zostawię to tam, aby ludzie wiedzieli, co oznacza ten komentarz.
Robert Massaioli
0

Zdaję sobie sprawę, że to bardzo stare pytanie, ale myślę, że jest inny powód, o którym nie wspomniano.

Jeśli deklarujesz nową funkcję bez punktów f . g . h, wartość, którą przekazujesz, zostanie zastosowana automatycznie. Jeśli jednak napiszesz f $ g $ h, to nie zadziała.

Myślę, że powodem, dla którego autor preferuje metodę kompozycji, jest to, że prowadzi ona do dobrej praktyki budowania funkcji.

hackeryarn
źródło