Jakie są zalety i wady stylu "point free" w programowaniu funkcyjnym?

Wiem, że w niektórych językach (Haskell?) dąży się do osiągnięcia stylu wolnego od punktów lub do nigdy jawnego odwoływania się do argumentów funkcji po nazwie. Jest to dla mnie bardzo trudna koncepcja do opanowania, ale może pomóc mi zrozumieć, jakie są zalety (a może nawet wady) tego stylu. Czy ktoś może to wyjaśnić?

Author: s952163, 0000-00-00

3 answers

Uważam, że celem jest być zwięzłym i wyrażać obliczenia pipelinowe jako kompozycję funkcji, a nie myśleć o threading argumenty poprzez. Prosty przykład (w F#) - podany:

let sum = List.sum
let sqr = List.map (fun x -> x * x)

Używane jak:

> sum [3;4;5]
12
> sqr [3;4;5]
[9;16;25]

Możemy wyrazić" sumę kwadratów " funkcji jako:

let sumsqr x = sum (sqr x)

I używać jak:

> sumsqr [3;4;5]
50

Lub możemy go zdefiniować przez Orurowanie x przez:

let sumsqr x = x |> sqr |> sum

Napisane w ten sposób, to oczywiste, że X jest przekazywane w tylko do być "gwintowane" przez sekwencję funkcji. Kompozycja bezpośrednia wygląda o wiele ładniej:

let sumsqr = sqr >> sum

Jest to bardziej zwięzłe i jest to inny sposób myślenia o tym, co robimy; komponowanie funkcji, a nie wyobrażanie sobie procesu przepływających argumentów. Nie opisujemy jak sumsqr działa. Opisujemy czym jest .

PS: ciekawym sposobem na opanowanie kompozycji jest wypróbowanie programowania w języku konkatenacyjnym, takim jak Forth, Joy, Factor itp. Mogą one być traktowane jako nic innego jak kompozycja (Forth : sumsqr sqr sum ;), w której przestrzeń między słowami jest operatorem composition.

PPS: być może inni mogliby skomentować różnice w wydajności. Wydaje mi się, że skład może zmniejszyć ciśnienie GC, czyniąc kompilator bardziej oczywistym, że nie ma potrzeby tworzenia wartości pośrednich, jak w pipelining; pomagając uczynić tak zwany "problem wylesiania" bardziej tractable.

 56
Author: AshleyF,
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-04-15 02:09:53

Styl wolny od punktów jest uważany przez niektórych autorów zaostateczny styl programowania funkcyjnego. Mówiąc prościej, funkcja typu t1 -> t2 opisuje przekształcenie z jednego elementu typu t1 w inny element typu t2. Chodzi o to, że funkcje" punktowe " (pisane za pomocą jawnych zmiennych) podkreślają elementy (pisząc \x -> ... x ..., opisujesz, co dzieje się z elementem x), podczas gdy funkcje "punktowe" (wyrażane bez użycia zmienne) podkreślają samą transformację jako kompozycję prostszych przekształceń. Zwolennik stylu wolnego od punktów argumentuje, że transformacje rzeczywiście powinny być centralną koncepcją i że wskazująca notacja, choć łatwa w użyciu, odwraca nas od tego szlachetnego ideału.

Programowanie funkcjonalne bez punktów jest dostępne od bardzo dawna. Była już znana przez logików, którzy studiowali logikę kombinacyjną od początkowego dzieła Mojżesza Schönfinkela w 1924 r. i stał się podstawą do pierwszych badań nad tym, co stanie się wnioskowaniem typu ML przez Roberta Feysa i... Haskell Curry w latach 50.]}

Pomysł budowania funkcji z ekspresyjnego zestawu podstawowych kombinatorów jest bardzo atrakcyjny i został zastosowany w różnych dziedzinach, takich jak języki manipulacji tablicami pochodzące z APL , lub biblioteki kombinatorów parserów, takie jak Parsec Haskella. W 2006 roku został wybrany na stanowisko profesora nadzwyczajnego. Backus . W swoim przemówieniu z 1978 roku "czy programowanie może być wyzwolone ze stylu Von Neumanna ?", napisał:

Wyrażenie lambda (wraz z regułami podstawienia) może definiowanie wszystkich możliwych funkcji obliczeniowych wszystkich możliwych typów i dowolnej ilości argumentów. Ta wolność i władza ma swoje wady, a także jego oczywiste zalety. Jest analogiczny do mocy nieograniczonych poleceń kontrolnych w konwencjonalnych języki: z nieograniczoną swobodą nadchodzi chaos. Jeśli jeden stale wymyśla nowe formy łączące, dopasowane do okazji, jak można w rachunku lambda, nie zaznajomić się z styl lub użyteczne właściwości kilku form łączących, które są odpowiednie do wszystkich celów. Tak samo jak programowanie strukturalne rezygnuje z wielu instrukcji sterowania, aby uzyskać programy o prostszych strukturę, lepsze właściwości i jednolite metody dla zrozumienie ich zachowania, więc programowanie funkcjonalne unika wyrażenie lambda, podstawienie i funkcja wielokrotna typy. Tym samym osiąga programy zbudowane ze znanych formy funkcjonalne o znanych właściwościach użytkowych. Programy te są tak skonstruowane, że ich zachowanie często można zrozumieć i udowodnione mechanicznym zastosowaniem technik algebraicznych podobnych do tych stosowany w rozwiązywaniu problemów algebry szkolnej.

Więc oto one. Główną zaletą programowania bez punktów jest to, że wymuszają zorganizowany styl kombinatora, który sprawia, że równania rozumowanie naturalne. Rozumowanie równościowe było szczególnie reklamowane przez zwolenników ruchu "Squiggol" (zob. [1] [2]) i rzeczywiście używać sprawiedliwego udziału kombinatorów bez punktów i reguł obliczania/przepisywania / rozumowania.

Wreszcie, jedną z przyczyn popularności programowania bez punktów wśród haskellites jest jego związek z teoria kategorii. W teorii kategorii morfizmy (które mogą być postrzegane jako "transformacje między obiektami") są podstawowym przedmiotem badań i obliczeń. Podczas gdy wyniki częściowe pozwalają na rozumowanie w określonych kategoriach, które mogą być wykonywane w stylu punktowym, powszechnym sposobem budowania, badania i manipulowania strzałkami jest nadal styl bez punktów i inne składnie, takie jak jak diagramy łańcuchów również wykazują tę "pointfreeness". Istnieją dość ścisłe powiązania między osobami propagującymi metody "algebry programowania" a użytkownikami kategorii w programowaniu (na przykład autorzy banana paper [2] są/byli hardcore categorists).

Może Cię zainteresować strona Pointfree Haskell wiki.

Minusy stylu pointfree są dość oczywiste: czytanie może być prawdziwym bólem. Powód, dla którego wciąż uwielbiamy używać zmiennych, pomimo licznych horrorów cieniowania, Alfa-równoważności itp., jest to zapis, który jest tak naturalny, aby czytać i myśleć. Ogólna idea jest taka, że złożona funkcja (w transparentnym języku odniesienia) jest jak złożony system hydrauliczny: wejścia są parametrami, dostają się do niektórych rur, są stosowane do funkcji wewnętrznych, powielane (\x -> (x,x)) lub zapomniane (\x -> (), rura prowadząca donikąd) itp. A notacja zmienna jest ładnie ukryta w całej tej maszynerii: dajesz nazwy na wejściu i nazwy na wyjściach (lub obliczenia pomocnicze), ale nie musisz opisywać całego planu hydraulicznego, gdzie małe rury pójdą, aby nie być przeszkodą dla większych, itp. Ilość kanalizacji wewnątrz czegoś tak krótkiego jak \(f,x,y) -> ((x,y), f x y) jest niesamowita. Możesz śledzić każdą zmienną indywidualnie lub czytać każdy pośredni węzeł hydrauliczny, ale nigdy nie musisz widzieć całej maszyny razem. Kiedy używasz Stylu bez punktów, to wszystko jest jednoznaczne, musisz napisać wszystko na dół, i spójrz na to później, a czasami jest po prostu brzydki.

PS: ta wizja jest ściśle związana z językami programowania stosu, które są prawdopodobnie najmniej przydatnymi językami programowania (ledwo) w użyciu. Rekomendowaĺ 'bym spróbowanie w nich zaprogramowaÄ ‡ tylko po to aby siÄ ™ w nich wczuÄ ‡ (tak jak zalecaĺ' bym programowanie logiczne). Zobacz Współczynnik, Kot lub czcigodny Forth.

 71
Author: gasche,
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
2013-01-19 08:36:22

Chociaż pociąga mnie koncepcja bez punktów i używam jej do pewnych rzeczy i zgadzam się ze wszystkimi pozytywami wspomnianymi wcześniej, uznałem te rzeczy za negatywne (niektóre są opisane powyżej): {]}

  1. Krótsza notacja zmniejsza redundancję; w silnie uporządkowanej kompozycji (ramda.styl js, czyli bez punktów w Haskell, czy w jakimkolwiek innym języku konkatenacyjnym) odczyt kodu jest bardziej złożony niż liniowe skanowanie przez wiązania const i używanie zakreślacza symboli do sprawdź, które Wiązanie przechodzi w jakie inne obliczenia. Poza strukturą drzewa i liniową, utrata opisowych nazw symboli sprawia, że funkcja jest trudna do intuicyjnego uchwycenia. Oczywiście zarówno struktura drzewa, jak i utrata nazwanych wiązań mają również wiele pozytywnych cech, na przykład funkcje będą czuć się bardziej ogólne-nie będą związane z jakąś domeną aplikacji poprzez wybrane nazwy symboli - a struktura drzewa jest semantycznie obecna nawet jeśli wiązania są ułożone i mogą być rozumiane sekwencyjnie (lisp let/let* style).

  2. Bezpunktowe jest najprostsze, gdy tylko przepuszcza lub komponuje szereg funkcji, ponieważ skutkuje to również liniową strukturą, którą my ludzie uważamy za łatwą do naśladowania. Jednak przeciąganie niektórych tymczasowych obliczeń przez wielu odbiorców jest żmudne. Istnieją wszelkiego rodzaju zawijanie w krotki, soczewkowanie i inne żmudne mechanizmy, aby po prostu udostępnić niektóre obliczenia, które w przeciwnym razie byłyby po prostu wielokrotnym użyciem jakiejś wartości wiążącej. Oczywiście powtórzona część może być wyodrębniona jako oddzielna funkcja i może to i tak dobry pomysł, ale istnieją również argumenty dla niektórych Nie-krótkich funkcji i nawet jeśli jest wyodrębniona, jej argumenty będą musiały być w jakiś sposób połączone przez obie aplikacje, a wtedy może być potrzeba zapamiętania funkcji, aby faktycznie nie powtarzać obliczeń. Przyda się wiele converge, lens, memoize, useWidth itd.

  3. JavaScript specific: harder to niechcący Debuguj. Dzięki liniowemu przepływowi let wiązań łatwo jest dodać punkt przerwania w dowolnym miejscu. Dzięki stylowi bez punktów, nawet jeśli w jakiś sposób dodano punkt przerwania, przepływ wartości jest trudny do odczytania, np. nie możesz po prostu odpytywać lub najeżdżać kursorem na jakąś zmienną w konsoli dewelopera. Ponadto, ponieważ point-free nie jest natywny w JS, funkcje biblioteki ramda.js lub podobne zasłonią stos dość dużo, zwłaszcza z obligate currying.

  4. Łamliwość kodu, szczególnie w systemach o niestandardowych rozmiarach i w produkcji. Jeśli pojawi się nowy element wymogu, powyższe wady wchodzą w grę (np. trudniej odczytać kod dla następnego opiekuna, który może być sobą przez kilka tygodni, a także trudniej prześledzić przepływ danych do wglądu). Ale co najważniejsze, nawet coś pozornie małego i niewinnego nowego wymogu może wymagać zupełnie innej struktury kodu. Można argumentować, że jest to dobra rzecz, ponieważ będzie to krystalicznie przejrzysta reprezentacja nowego rzecz, ale przepisywanie dużych fragmentów kodu bez punktów jest bardzo czasochłonne i nie wspominaliśmy o testowaniu. Wydaje się więc, że luźniejsze, mniej ustrukturyzowane, leksykalne kodowanie oparte na zadaniach może być szybciej przekształcone. Szczegăłlnie jeĹ " li kodowanie jest eksploracyjne, a w dziedzinie danych ludzkich o dziwnych konwencjach (czas itp. jest to bardzo ważne, ponieważ nie jest to konieczne, ponieważ nie jest to konieczne do osiągnięcia zamierzonego celu.]}

 2
Author: ,
Warning: date() expects parameter 2 to be long, string given in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54