Przeczytałem kilka tekstów o programowaniu deklaratywnym / funkcjonalnym (językach), sam wypróbowałem Haskell, a także sam napisałem. Z tego, co widziałem, programowanie funkcjonalne ma kilka zalet w stosunku do klasycznego stylu imperatywnego:
- Programy bezpaństwowe; Brak efektów ubocznych
- Konkurencja; Niezwykle dobrze gra z rosnącą technologią wielordzeniową
- Programy są zwykle krótsze, a w niektórych przypadkach łatwiejsze do odczytania
Wydajność rośnie (przykład: Erlang)
Programowanie imperatywne to bardzo stary paradygmat (o ile mi wiadomo) i być może nie nadaje się do XXI wieku
Dlaczego firmy używające programów napisanych w językach funkcjonalnych nadal są tak „rzadkie”?
Dlaczego, patrząc na zalety programowania funkcjonalnego, nadal używamy imperatywnych języków programowania?
Może było na to za wcześnie w 1990 roku, ale dziś?
functional-programming
pankrax
źródło
źródło
Odpowiedzi:
Ponieważ wszystkie te zalety są również wadami.
W rzeczywistych programach chodzi o efekty uboczne i mutacje. Gdy użytkownik naciska przycisk, dzieje się tak dlatego, że chce, aby coś się wydarzyło. Kiedy coś wpisują, chcą, aby ten stan zastąpił to, co kiedyś tam było. Kiedy Jane Smith w księgowości bierze ślub i zmienia nazwisko na Jane Jones, baza danych wspierająca proces biznesowy, który drukuje jej wypłatę, powinna lepiej polegać na obsłudze tego rodzaju mutacji. Kiedy strzelasz z karabinu maszynowego w kosmitę, większość ludzi nie modeluje go mentalnie jako budowy nowego kosmity z mniejszą liczbą punktów życia; modelują to jako mutację istniejących właściwości obcego.
Kiedy pojęcia języka programowania zasadniczo działają przeciwko modelowanej domenie, trudno uzasadnić użycie tego języka.
Problem został tylko rozwiązany. Dzięki niezmiennym strukturom danych masz tanie bezpieczeństwo wątków kosztem ewentualnej pracy z przestarzałymi danymi. Zmienne struktury danych mają tę zaletę, że zawsze pracują nad świeżymi danymi, kosztem pisania skomplikowanej logiki, aby zachować spójność danych. To nie tak, że jeden z nich jest oczywiście lepszy od drugiego.
Z wyjątkiem przypadków, gdy są one dłuższe i trudniejsze do odczytania. Nauka czytania programów napisanych w funkcjonalnym stylu jest trudną umiejętnością; wydaje się, że ludzie są znacznie lepsi w postrzeganiu programów jako serii kroków do wykonania, takich jak przepis, niż jako seria obliczeń, które należy wykonać.
Wydajność musi znacznie wzrosnąć, aby uzasadnić ogromne koszty związane z zatrudnieniem programistów, którzy wiedzą, jak programować w funkcjonalny sposób.
I pamiętaj, że nie chcesz wyrzucać działającego systemu; większość programistów nie buduje nowych systemów od zera, a raczej utrzymuje istniejące systemy, z których większość została zbudowana w językach niefunkcjonalnych. Wyobraź sobie próbę uzasadnienia tego akcjonariuszom. Dlaczego zeskrobaliście istniejący działający system płac, aby zbudować nowy kosztem milionów dolarów? „Ponieważ programowanie funkcjonalne jest niesamowite” raczej nie zachwyci akcjonariuszy.
Programowanie funkcjonalne jest również bardzo stare. Nie rozumiem, jak ważny jest wiek tego pomysłu.
Nie zrozum mnie źle. Uwielbiam programowanie funkcjonalne, dołączyłem do tego zespołu, ponieważ chciałem pomóc wprowadzić koncepcje programowania funkcjonalnego do C # i myślę, że programowanie w niezmiennym stylu jest drogą przyszłości. Ale programowanie w funkcjonalnym stylu wiąże się z ogromnymi kosztami , których nie można po prostu odrzucić. Przejście w kierunku bardziej funkcjonalnego stylu nastąpi powoli i stopniowo w ciągu dziesięcioleci. I to właśnie będzie: przejście do bardziej funkcjonalnego stylu, a nie hurtowe obejmowanie czystości i piękna Haskell oraz rezygnacja z C ++.
Buduję kompilatory na życie i zdecydowanie popieramy funkcjonalny styl dla następnej generacji narzędzi kompilatora. Wynika to z faktu, że programowanie funkcjonalne jest zasadniczo dobrym rozwiązaniem dla problemów, z którymi mamy do czynienia. Nasze problemy dotyczą pobierania surowych informacji - ciągów i metadanych - i przekształcania ich w różne ciągi i metadane. W sytuacjach, w których występują mutacje, tak jak ktoś wpisuje IDE, przestrzeń problemowa z natury nadaje się do technik funkcjonalnych, takich jak stopniowa odbudowa tylko części drzewa, które uległy zmianie. Wiele domen nie ma tych miłych właściwości, które sprawiają, że są one oczywiście podatne na funkcjonalny styl .
źródło
Masterminds of Programming: Rozmowy z twórcami głównych języków programowania
źródło
Podstawowa odpowiedź jest taka, że ani nie zastąpią, ani nie powinny zastąpić innych - są to różne narzędzia, które mają różne zestawy zalet i wad, i które mają przewagę, będą się różnić w zależności od projektu i innych „miękkich” kwestii, takich jak dostępna pula talentów.
Myślę, że masz rację, że wzrost współbieżności spowodowany wielordzeniowym zwiększeniem procentu (globalnego zestawu projektów programistycznych), gdy programowanie funkcjonalne zostanie wybrane zamiast innych stylów.
Myślę, że dzisiaj jest to rzadkie, ponieważ większość dzisiejszej profesjonalnej puli talentów jest najbardziej komfortowa dzięki imperatywnym i obiektowym technologiom. Na przykład niejednokrotnie wybrałem Javę jako język dla komercyjnego projektu, ponieważ był on wystarczająco dobry, nie budzący kontrowersji i wiem, że nigdy nie zabraknie ludzi, którzy mogą programować (wystarczająco dobrze) w nim.
źródło
Pomimo zalet programowania funkcjonalnego, programowanie imperatywne i obiektowe nigdy nie zniknie całkowicie.
Programowanie imperatywne i obiektowe to szczegółowy opis problemu i jego rozwiązania. Jako takie może być łatwiejsze do zrozumienia. Programowanie funkcjonalne może być nieco niejasne.
Ostatecznie przydatny program zawsze będzie miał skutki uboczne (takie jak zapewnienie rzeczywistej wydajności użytkownika do konsumpcji), więc najczystsze z języków funkcjonalnych nadal będą musiały od czasu do czasu wejść w imperatywny świat.
Obecny stan techniki polega na tym, że języki imperatywne (takie jak C #) zapożyczają funkcje ze świata funkcjonalnego (takie jak wyrażenia lambda) i odwrotnie.
źródło
Czyż nie
Smalltalk był świetnym systemem obiektowym w tamtych czasach. Dlaczego nie przejęło programowania obiektowego? Cóż, ma. Po prostu nie wygląda jak Smalltalk. Języki głównego nurtu stają się coraz bardziej podobne do Smalltalk dzięki C ++, Java, C # itp. Moda i styl zmieniają się wolniej niż cokolwiek innego, więc kiedy OO weszło do głównego nurtu, dostaliśmy go poprzez przyklejenie części OO do starych języków, więc wyglądało to tak, jakby C mógł przełknąć .
Funkcjonalność jest taka sama. Haskell był świetnym językiem funkcjonalnym. Ale dziś mamy jeszcze większą masę programistów głównego nurtu stosujących składnię podobną do C niż 20 lat temu. Musi więc wyglądać jak C. Gotowe: spójrz na dowolne wyrażenie LINQ i powiedz, że nie działa.
źródło
dynamic
. To najbardziej Smalltalk-y z tych funkcji, więc nie jest dla mnie zaskoczeniem, że jest ona obecna tylko w najnowszej wersji najnowocześniejszego z tych trzech języków. :-)Wierzę, że imperatywne języki są bardziej rozpowszechnione po prostu dlatego, że do tego przywykło więcej ludzi. Ani programowanie funkcjonalne, ani model programowania imperatywnego nie są bardziej niejasne ani akademickie niż inne. W rzeczywistości są uzupełnieniami.
Jeden z plakatów mówi, że kod imperatywny jest łatwiejszy do zrozumienia niż kod programowania funkcjonalnego. Jest to prawdą tylko wtedy, gdy czytelnik już widział kod rozkazujący, zwłaszcza jeśli poprzednie przykłady należą do tej samej „rodziny” (na przykład C / C ++, Perl, PHP i Java). Nie twierdziłbym, że dotyczy to jakiegokolwiek imperatywnego języka; porównaj Javę i Fortha, by dać ekstremalny przykład.
Dla laika wszystkie języki programowania są nieczytelnym bełkotem, z wyjątkiem być może pełnych języków, takich jak Hypertalk i SQL. (Warto zauważyć, że SQL jest deklaratywnym i / lub funkcjonalnym językiem i cieszy się ogromną popularnością.)
Gdybyśmy od początku byli szkoleni w języku Lisp-y lub Haskell-y, wszyscy uważalibyśmy, że funkcjonalne języki programowania są całkowicie normalne.
źródło
Masz już wystarczająco dużo odpowiedzi, że wymienię tylko kilka rzeczy, o których jeszcze nie wspomniałem.
Po pierwsze i (moim zdaniem), języki proceduralne odniosły ogromne korzyści ze stopnia ich podobieństwa. Na przykład, prawie każdy, kto zna prawie jeden z głównych języków proceduralnych (lub OO) w prawie każdym stopniu, potrafi dobrze odczytać większość pozostałych. Aktywnie unikam pracy w Javie, C #, Cobolu, Fortranie lub Basicu (tylko kilka przykładów), ale potrafię czytać dowolne z nich dość dobrze - prawie tak samo dobrze, jak ludzie, którzy używają ich na co dzień.
Od strony funkcjonalnej jest to o wiele mniej prawdziwe. Na przykład mogę dość dobrze napisać Schemat, ale nie ma to większego sensu w czytaniu Ocaml lub Haskell (tylko dla kilku przykładów). Nawet w obrębie jednej rodziny (np. Scheme vs. Common Lisp) znajomość jednej nie wydaje się przekładać tak dobrze na drugą.
Twierdzenie, że kod funkcjonalny jest bardziej czytelny, wydaje się prawdziwe tylko w wąskim zakresie warunków. Dla osób, które są bardzo obeznane z językiem, czytelność jest rzeczywiście doskonała - ale dla wszystkich innych często nie istnieje. Co gorsza, podczas gdy różnice w językach proceduralnych są w dużej mierze składniowe, a zatem względnie łatwe do nauczenia się, różnice w językach funkcjonalnych są często znacznie bardziej fundamentalne, dlatego wymagają dogłębnej analizy, aby naprawdę zrozumieć (np. Wiedzieć, że Lisp niewiele pomaga w zrozumieniu monad).
Inną ważną kwestią jest to, że idea, że programy funkcjonalne są krótsze od programów proceduralnych, często opiera się bardziej na składni niż semantyce. Programy napisane w Haskell (na przykład) są często dość krótkie, ale ich funkcjonalność jest raczej niewielką częścią tego. Świetna okazja, jeśli po prostu Haskell ma stosunkowo zwięzłą składnię.
Niewiele czysto funkcjonalnych języków może dobrze konkurować z APL o zwięzły kod źródłowy (chociaż, uczciwie, APL obsługuje również tworzenie funkcji wyższego poziomu, więc nie jest to duża różnica, jak w niektórych innych przypadkach). Przeciwnie, Ada i C ++ (tylko dla kilku przykładów) mogą być dość konkurencyjne pod względem liczby operacji niezbędnych do wykonania danego zadania, ale składnia jest (przynajmniej zwykle) znacznie bardziej szczegółowa.
źródło
Brak odczuwanej potrzeby
Przypominam sobie odpowiedź mojego starego szefa Ricka Cline'a, kiedy pokazałem mu kopię wykładu Turinga Johna Backusa zatytułowanego Czy programowanie może zostać wyzwolone ze stylu von Neumanna?
Jego odpowiedź: „Może niektórzy z nas nie chcą wyzwolić się ze stylu von Neumanna!”
źródło
Funkcjonalność jest lepsza dla niektórych rzeczy, a gorsza dla innych, więc nigdy nie „przejmie”. Jednak w świecie rzeczywistym jest już wszechobecny.
Programy bezstanowe są łatwiejsze do przetestowania. Jest to obecnie powszechnie doceniane i często wykorzystywane w przemyśle.
Łączysz współbieżność i równoległość.
Współbieżność można efektywnie realizować za pomocą komunikujących się procesów sekwencyjnych (CSP). Kod wewnątrz CSP może mutować jego stan lokalny, ale wiadomości wysyłane między nimi powinny zawsze być niezmienne.
Czysto funkcjonalne programowanie gra bardzo źle z multicore, ponieważ jest tak nieprzyjazny dla pamięci podręcznej. Rdzenie walczą o pamięć współdzieloną, a programy równoległe nie skalują się.
Scala jest często uważany za język funkcjonalny, ale nie jest on bardziej funkcjonalny niż C #, który jest obecnie jednym z najpopularniejszych języków na świecie.
Czysto funkcjonalne programowanie ma wiele poważnych wad, dlatego używamy nieczystych języków funkcjonalnych, takich jak Lisp, Scheme, SML, OCaml, Scala i C #.
źródło
Kiedy zastanawiam się, co programowanie funkcjonalne może przynieść moim projektom w pracy, zawsze podążam tą samą ścieżką myślenia:
Aby uzyskać pełne korzyści z programowania funkcjonalnego, potrzebujesz lenistwa. Tak, istnieją ścisłe języki funkcjonalne, ale prawdziwe zalety programowania funkcjonalnego nie świecą tak dobrze w ścisłym kodzie. Na przykład w Haskell łatwo jest utworzyć sekwencję leniwych operacji na liście, połączyć je i zastosować do listy. Na przykład.
op1 $ op2 $ op3 $ op4 $ someList
. Wiem, że to nie zbuduje całej listy i wewnętrznie po prostu otrzymam ładną pętlę, która przechodzi przez elementy pojedynczo. To pozwala pisać naprawdę modułowy kod. Interfejs między dwoma modułami może obejmować przekazywanie potencjalnie rozległych struktur danych, a mimo to nie musisz rezydować w strukturze.Ale kiedy masz lenistwo, trudno jest uzasadnić wykorzystanie pamięci. Zmiana flag kompilatora Haskell często zmienia ilość pamięci używanej przez algorytm z O (N) na O (1), z tym że czasami tak nie jest. Jest to nie do zaakceptowania, gdy masz aplikacje, które muszą maksymalnie wykorzystać całą dostępną pamięć, i nie jest świetne nawet dla aplikacji, które nie potrzebują całej pamięci.
źródło
Dwie rzeczy:
źródło