Jeśli potrafimy programować funkcjonalnie za pomocą Pythona, czy potrzebujemy konkretnego funkcjonalnego języka programowania? [Zamknięte]

22

Za pomocą generatorów i lambda możemy programować funkcjonalnie za pomocą Pythona. To samo możesz osiągnąć również dzięki Ruby.

Pytanie zatem brzmi: dlaczego potrzebujemy konkretnych funkcjonalnych języków programowania, takich jak Erlang, Haskell i Scheme? Czy jest coś innego niż te specyficzne funkcjonalne języki programowania? Dlaczego nie możemy po prostu używać Pythona do programowania funkcjonalnego?

Joshua Partogi
źródło
30
wszystkie z nich istniały jeszcze przed stworzeniem Pythona
Mahmoud Hossam
51
Ford Pinto to samochód. Dlaczego potrzebujemy konkretnych szybkich samochodów, takich jak Ferraris?
Martin York,
11
Używając klas i szablonów, możemy zrobić wszystko OO w C ++. Dlaczego Java i Python zostały stworzone? Co oni dodają?
9000
19
Wszystkie języki programowania (oprócz niektórych czysto akademickich języków badawczych) są odpowiednikami Turinga, więc to, co możesz zrobić w języku A, możesz zrobić w dowolnym innym języku. Tak więc, zgodnie z tym tokiem
Maglob
17
Jeśli znasz któryś z tych języków, nie twierdziłbyś, że Python dobrze programuje funkcjonalnie. Nie ma Robi to wystarczająco dobrze, aby uwzględnić część rzeczy FP, ale nie lepiej.

Odpowiedzi:

20

Doceniam to pytanie, ponieważ osobiście jestem wielkim fanem zarówno języka Python, jak i funkcjonalnego stylu programowania. Mam duże doświadczenie w Pythonie i ostatnio zacząłem uczyć się języka Haskell, więc oto kilka punktów opartych na moich osobistych doświadczeniach na temat różnic między tymi językami, z funkcjonalnego punktu widzenia.

Czystość

Nawet jeśli nie zależy ci na czystości funkcji (tj. Braku efektów ubocznych) jako zasady, ma to praktyczny wpływ na to, jak łatwo można odczytać kod i powód. Nawet jeśli utrzymujesz czystość we własnych funkcjach Pythona, istnieje duża różnica w wymuszaniu czystości przez kompilator, a przede wszystkim w posiadaniu standardowej biblioteki zbudowanej w oparciu o czystość i niezmienne struktury danych.

Wydajność

W zależności od domeny aplikacji możesz, ale nie musi to zależeć od wydajności, ale pisanie statyczne i gwarantowana czystość daje kompilatorowi znacznie więcej możliwości pracy w porównaniu z Pythonem i innymi dynamicznymi językami (chociaż muszę przyznać, że PyPy świetnie sobie radzi inroads, i np. LuaJIT graniczy z cudami).

Optymalizacja ogona

Związane z wydajnością, ale nieco inne. Nawet jeśli nie przejmujesz się zbytnio wydajnością środowiska wykonawczego, brak optymalizacji wywołania ogona (szczególnie w przypadku rekurencji ogona) ogranicza sposoby implementacji algorytmów w Pythonie bez przekraczania limitów stosu.

Składnia

Jest to największy powód, dla którego zacząłem patrzeć na „prawdziwe” języki funkcjonalne zamiast po prostu używać Pythona z funkcjonalnym stylem. Chociaż myślę, że Python ma ogólnie bardzo ekspresyjną składnię, ma jednak pewne słabe punkty specyficzne dla kodowania funkcjonalnego. Na przykład:

  • Składnia funkcji lambda jest dość szczegółowa i ograniczona w tym, co mogą zawierać
  • Brak cukru syntaktycznego dla składu funkcji tj. f = g . hVs.f = lambda *arg: g(h(*arg))
  • Bez cukru syntaktycznego do częściowego zastosowania, tj. f = map gVs.f = functools.partial(map, g)
  • Brak cukru syntaktycznego do używania operatorów infix w funkcjach wyższego rzędu, tj. sum = reduce (+) lstVs.sum = reduce(operator.add, lst)
  • Brak dopasowania wzorca lub ochrona argumentów funkcji, które ułatwiają wyrażanie warunków końca rekurencji i niektórych przypadków granicznych z bardzo czytelną składnią.
  • Nawiasy nigdy nie są opcjonalne dla wywołań funkcji, a dla wywołań zagnieżdżonych nie ma cukru składniowego. Wydaje mi się, że jest to kwestia gustu, ale szczególnie w kodzie funkcjonalnym często zdarza się , że łączą wywołania funkcji i y = func1 $ func2 $ func3 xłatwiej jest je odczytać niż y = func1(func2(func3(x)))po zapoznaniu się z tą notacją.
shang
źródło
28

Oto najważniejsze różnice:

Haskell

  • Leniwa ocena
  • Kompiluje się do kodu maszynowego
  • Wpisywanie statyczne zapewnia czystość funkcji
  • Wnioskowanie typu

Haskell i Erlang

  • Dopasowywanie wzorów

Erlang

  • Aktorowy model współbieżności, lekkie procesy

Schemat

  • Makra

Wszystkie języki

  • prawdziwe zamknięcia (Ruby ma zamknięcia, czy python może być dyskutowany, zobacz komentarze)
  • standardowa biblioteka odpowiednia do funkcjonalnego stylu programowania (niezmienne zbiory, mapa, filtr, składanie itp.)
  • rekursja ogona (można to również znaleźć w niektórych językach niefunkcjonalnych)

Ponadto powinieneś rzucić okiem na języki z rodziny ML, takie jak SML, Ocaml i F # i Scala, które łączą OO i programowanie funkcjonalne w nowy sposób. Wszystkie te języki mają unikalne interesujące funkcje.

Kim
źródło
3
+1 dobry post. Możesz dodać wnioskowanie typu w procesach Haskell i Light-weight w Erlang.
Jonas
1
Python ma funkcję mapowania, filtrowania i składania (zmniejszania). Odnośnie „prawdziwych zamknięć”: jeśli zdefiniujesz prawdziwe zamknięcie jako zamknięcie, które może zawierać coś innego niż pojedyncze wyrażenie, Haskell również nie ma prawdziwych zamknięć (ale oczywiście Haskell ma kilka rzeczy, które nie są wyrażeniami ...) . Jednak kwestia niezmiennych struktur danych jest dobra, więc +1 za to. Również: rekurencja ogona (i ogólnie tańsza rekurencja).
sepp2k
2
+1 dla „pisania statycznego zapewnia, że ​​funkcje są czyste”. Bardzo fajnie jest mieć system typów, który rozróżnia funkcje czyste i nieczyste. (Cnota const doe snot count. :)
Macke
1
@btilly: Nie uważałbym tego za zamknięcie, jeśli nie można przypisać do zmiennej o zasięgu. W przeciwnym razie musielibyśmy powiedzieć, że Java ma również zamknięcia, ponieważ można tam zastosować tę samą sztuczkę.
Kim
3
W zamknięciu mogę uzyskać dostęp do zmiennej w taki sam sposób, jak zwykle. Dotyczy to zamknięć Haskella i Ruby, ale nie złych zamienników Pythona lub Javy. Może ktoś inny może nas oświecić o erlangu, nie znam tego zbyt dobrze.
Kim
19

Trudno jest dokładnie zdefiniować, czym jest „język funkcjonalny” - spośród wymienionych języków tylko Haskell jest czysto funkcjonalny (wszystkie inne mają podejście hybrydowe). Istnieją jednak pewne funkcje językowe, które są bardzo pomocne w programowaniu funkcjonalnym, a Ruby i Python nie mają ich wystarczająco dużo, aby były bardzo dobrym środowiskiem dla FP. Oto moja osobista lista kontrolna, w kolejności według ważności:

  1. Funkcje i zamknięcia najwyższej klasy (Ruby, Python i wszystkie inne wymienione na liście mają to).
  2. Gwarantowana optymalizacja wywołania ogona (Erlang, Haskell, Scala i Scheme mają to, ale nie Python, Ruby ani Clojure (jeszcze)).
  3. Obsługa niezmienności w języku i bibliotekach standardowych (jest to duży język, który mają wszystkie wymienione „języki funkcjonalne” (oprócz schematu), ale Ruby i Python nie).
  4. Obsługa na poziomie językowym referencyjnie przejrzystych (lub czystych) funkcji (o ile mi wiadomo, tylko Haskell ma to obecnie).

Konieczność (1) powinna być oczywista - funkcje wyższego rzędu są niezwykle trudne bez funkcji pierwszej klasy. Kiedy ludzie mówią, że Ruby i Python są dobrymi językami dla FP, zwykle mówią o tym. Jednak ta szczególna cecha jest konieczna, ale niewystarczająca, aby język był dobry dla programu ramowego.

(2) jest tradycyjną koniecznością dla FP, odkąd wynaleziono Schemat. Bez TCO niemożliwe jest programowanie z głęboką rekurencją, która jest jednym z kamieni węgielnych FP, ponieważ dostajesz przepełnienia stosu. Jedynym „funkcjonalnym” (według popularnej definicji) językiem, który go nie ma, jest Clojure (z powodu ograniczeń JVM), ale Clojure ma wiele różnych metod symulowania TCO. (FYI, Ruby TCO jest specyficzny dla implementacji , ale Python konkretnie go nie obsługuje ). Powodem, dla którego TCO musi być zagwarantowane, jest to, że jeśli jest specyficzne dla implementacji, głębokie funkcje rekurencyjne zepsują się z niektórymi implementacjami, więc nie można tak naprawdę w ogóle ich używaj.

(3) to kolejna wielka rzecz, którą współczesne języki funkcjonalne (zwłaszcza Haskell, Erlang, Clojure i Scala) mają w przeciwieństwie do Ruby i Pythona. Bez wchodzenia w zbyt wiele szczegółów, gwarantowana niezmienność eliminuje całe klasy błędów, szczególnie w sytuacjach współbieżnych, i pozwala na porządne rzeczy, takie jak trwałe struktury danych . Bardzo trudno jest skorzystać z tych korzyści bez wsparcia na poziomie językowym.

(4) jest dla mnie najbardziej interesującą rzeczą w językach czysto funkcjonalnych (w przeciwieństwie do języków hybrydowych). Rozważ następującą niezwykle prostą funkcję Ruby:

def add(a, b)
  a + b
end

Wygląda to na czystą funkcję, ale z powodu przeciążenia operatora może mutować albo parametr, albo powodować działania niepożądane, takie jak drukowanie na konsoli. Jest mało prawdopodobne, aby ktoś przeciążył +operatora, aby wywołać efekt uboczny, ale język nie daje żadnych gwarancji. (To samo dotyczy Pythona, choć może nie w tym konkretnym przykładzie).

Z drugiej strony w języku czysto funkcjonalnym istnieją gwarancje na poziomie językowym, że funkcje są względnie przejrzyste. Ma to wiele zalet: proste funkcje można łatwo zapamiętać; można je łatwo przetestować bez polegania na jakimkolwiek stanie globalnym; a wartości w obrębie funkcji można oceniać leniwie lub równolegle bez obawy o problemy z współbieżnością. Haskell w pełni z tego korzysta, ale nie znam wystarczającej liczby języków funkcjonalnych, aby wiedzieć, czy tak.

Mimo to można stosować techniki FP w prawie każdym języku (nawet Java). Na przykład Google MapReduce jest inspirowany funkcjonalnymi pomysłami, ale o ile wiem, nie używają żadnych „funkcjonalnych” języków w swoich dużych projektach (myślę, że głównie używają C ++, Java i Python).

shosti
źródło
2
+1 za dokładne wyjaśnienie - nawet ja jako outsider FP zrozumiałem to. Dzięki! :-)
Péter Török
najcenniejsza jak dotąd odpowiedź na to pytanie. Oparte na faktach i wielu informacjach. Dobra robota.
wirrbel
Scala ma rekurencję ogona i, podobnie jak w Scheme, odbywa się to automatycznie, jeśli wywołanie rekurencyjne znajduje się w pozycji ogona (w przeciwieństwie do Clojure, gdzie należy to wyraźnie zażądać). Jest nawet adnotacja, dzięki czemu można sprawdzić, że kompilator będzie generować kod ogon rekurencyjnej. To, czego nie ma, to bardziej ogólny TCO Scheme. Zakładam, że o tym wiesz, ale skoro włożyłeś tyle szczegółów w większość innych rzeczy, wydawało się to dziwnym lekceważeniem / zaniechaniem.
itsbruce,
@itsbruce Ten post jest dość stary, IIRC Scala nie miał go w tym czasie (a może po prostu się myliłem;). Zaktualizowano
shosti
Nie używałem Scali od samego początku, ale miałam rekursję ogona w 2008 roku, kiedy się zainteresowałem ;-) Odsyłam osoby, które pytają o ten temat do tego konkretnego pytania SO, ponieważ ma kilka dobrych odpowiedzi, a ja tylko zauważyłem tę dziwność, więc skomentowałem dla kompletności.
itsbruce
11

Wymienione języki są bardzo różne.

Podczas gdy Python i Ruby są dynamicznie pisanymi językami, Haskell jest wpisywany statycznie. Erlang jest językiem współbieżnym i wykorzystuje model aktora i bardzo różni się od wszystkich innych języków, o których wspominasz.

Python i Ruby mają wiele konstrukcji imperatywnych, podczas gdy w bardziej czystym języku funkcjonalnym, takim jak Haskell, wszystko zwraca coś lub innymi słowy wszystko jest funkcją.

Jonas
źródło
@ kRON: Cóż, system pisma jest ważną właściwością języka i zapytał: „Czy jest coś innego, co zapewniają te konkretne funkcjonalne języki programowania?”. Jasne, możesz używać modelu Actor z innymi językami, ale Erlang ma wbudowaną wersję językową. Erlang wykorzystuje lekkie procesy i ma wbudowane konstrukcje językowe do programowania rozproszonego - stąd język „współbieżny”.
Jonas
8

Jak zwykle spóźnia się na przyjęcie, ale i tak coś powie.

Funkcjonalny język programowania nie jest językiem, który umożliwia programowanie funkcjonalne. Gdybyśmy mieli stosować tę definicję, praktycznie każdy język w dowolnym miejscu jest funkcjonalnym językiem programowania. (Nawiasem mówiąc, to samo dotyczy OOP. Możesz pisać w stylu OOP w C. Jeśli chcesz, to zgodnie z logiką C jest językiem OOP.)

To, co sprawia, że ​​funkcjonalny język programowania nie jest tym, na co pozwala ci programować, jest tym, na co pozwala ci programować w prosty sposób . To jest klucz.

Python ma więc lambdy (które są niezwykle anemicznymi sprawami przypominającymi zamknięcie) i oferuje kilka funkcji bibliotecznych, które zobaczysz w bibliotekach funkcjonalnych, a także „map” i „fold”. Jednak to nie wystarczy, aby uczynić go funkcjonalnym językiem programowania, ponieważ trudno jest konsekwentnie programować w nim odpowiedni funkcjonalny styl (a język z pewnością nie wymusza tego stylu!). W jego rdzeniu Python jest imperatywnym językiem związanym z operacjami stanu i manipulowania stanem, co jest po prostu sprzeczne z semantyką wyrażeń i oceny wyrażeń w języku funkcjonalnym.

Dlaczego więc mamy funkcjonalne języki programowania, skoro Python (lub Ruby (lub wstaw wybrany język)) może „robić programowanie funkcjonalne”? Ponieważ Python i inni nie potrafią poprawnie zaprogramować funkcjonalnie. Dlatego.

PO PROSTU MOJA poprawna OPINIA
źródło
6

Państwo może zrobić programowania funkcyjnego w Javie (patrz np http://functionaljava.org/ ). Można również zrobić programowanie obiektowe w C . To po prostu nie takie idiomatyczne.

Rzeczywiście nie potrzebujemy absolutnie Erlanga, Haskella, Scheme ani żadnego konkretnego języka programowania, ale wszystkie reprezentują różne podejścia i różne kompromisy, dzięki czemu niektóre zadania są łatwiejsze, a niektóre trudniejsze. To, czego powinieneś użyć, zależy od tego, co chcesz osiągnąć.

Joonas Pulakka
źródło
4

To pytanie można zastosować do nieskończonej liczby języków i paradygmatów.

  • Ponieważ wszyscy używają C ++, dlaczego potrzebujemy innych języków ogólnego przeznaczenia?
  • Skoro java jest świetnym językiem OO, dlaczego istnieją inne języki OO?
  • Ponieważ Perl jest niesamowitym językiem skryptowym, dlaczego potrzebujemy Pythona?
  • Yatta, Yatta, Yatta

Większość, jeśli nie wszystkie języki istnieją z konkretnego powodu. Istnieją, ponieważ ktoś potrzebował, aby żaden obecny język nie był wypełniony lub źle wypełniony. (To oczywiście nie dotyczy wszystkich języków, ale uważam, że dotyczy większości znanych języków.) Na przykład Python został pierwotnie opracowany do współpracy z systemem operacyjnym Amoeba [ 1 , 2 ], a Erlang został stworzony, aby pomóc w rozwoju aplikacji telefonicznych [ 3 ]. Tak więc jedna odpowiedź na pytanie „Dlaczego potrzebujemy innego języka funkcjonalnego?” może po prostu być, ponieważ [insert-name-of-someone-who-know-how-to-design-languages] nie podobało się to, w jaki sposób Python to zrobił.

To właściwie podsumowuje, jak sądzę, odpowiedź. Chociaż możesz zrobić wszystko za pomocą Pythona, co możesz zrobić za pomocą funkcjonalnego języka, to czy naprawdę chcesz? Wszystko, co możesz zrobić w C, możesz zrobić w asemblerze, ale czy chcesz? Różne języki zawsze będą najlepsze w robieniu różnych rzeczy i tak właśnie powinno być.

cledoux
źródło
2

Programowanie funkcjonalne dotyczy zarówno paradygmatu projektowania, jak i konkretnych funkcji językowych. Innymi słowy, lambdas i funkcja mapy nie tworzą funkcjonalnego języka programowania. Python i Ruby mają pewne funkcje inspirowane funkcjonalnymi językami programowania, ale nadal piszesz kod w bardzo imperatywny sposób. (To coś w stylu C: możesz pisać kod podobny do OO w C, ale nikt poważnie nie uważa C za język OO.)

Spójrz: programowanie funkcjonalne to nie tylko lambda lub mapfunkcje wyższego rzędu. Chodzi o projekt . Program napisany w „prawdziwym” funkcjonalnym języku programowania rozwiązuje problemy poprzez kompozycję funkcji. Chociaż programy napisane w Ruby lub Python mogą korzystać z funkcji podobnych do FP, zazwyczaj nie czytają jak zestaw złożonych funkcji.

mipadi
źródło
0

Każdy język funkcjonalny, o którym wspomniałeś, całkiem dobrze wpasowuje się w pewną niszę, a wzorce idiomatyczne, które każdy zachęca, sprawiają, że bardzo dobrze nadają się do pewnych zadań, których nie można byłoby wykonać w Pythonie, chyba że zbudujesz ogromną bibliotekę modułów pomocniczych. Najbardziej oczywistą z takich niszowych doskonałości jest model współbieżności Erlanga. Pozostałe mają również podobne zalety.

davidk01
źródło
0

Każda koncepcja dostępna w rachunku lambda, LISP i schemacie jest dostępna w Pythonie, więc tak, możesz w niej programować funkcjonalnie. To, czy jest to wygodne, czy nie, zależy od kontekstu i smaku.

Możesz łatwo napisać interpreter LISP (lub inny język funkcjonalny) w Pythonie (Ruby, Scala) (co to oznacza?). Możesz napisać interpreter dla Pythona, używając czysto funkcjonalnego, ale zajęłoby to dużo pracy. Nawet języki „funkcjonalne” są obecnie wieloparadygmatyczne.

Ta stara i piękna książka ma większość (choć nie wszystkie) odpowiedzi na temat tego, na czym polega istota programowania funkcjonalnego.

Apalala
źródło
@Arkaaito Ale czy zgadzasz się, że każda koncepcja dostępna w rachunku lambda, LISP i schemacie jest dostępna w Pythonie?
Mark C
Czy jesteś pewien, że każda koncepcja seplenienia jest dostępna w Pythonie? Makra, kod to dane?
SK-logic
@ SK-logic Makra nie są w rachunku lambda, ale tak, są dostępne w Pythonie, choć nie pod tą nazwą. Python udostępnia eval()funkcję, która jest potrzebna, aby kod zawierał dane , ale idzie dalej: pozwala modyfikować większość środowiska wykonawczego, np. W LISP.
Apalala
@Apalala, języki dynamiczne eval()to metaprogramowanie środowiska wykonawczego. Czasami jest przydatny, ale jest niezwykle drogi. Makra Lisp są różne, jest to metaprogramowanie czasu kompilacji. Nie jest dostępny w Pythonie w żadnej użytecznej formie.
SK-logika
@ SK-logic Tego rodzaju makra łamią przesłankę kod-dane , ponieważ programy nie mogą zmieniać makr (ani nawet wiedzieć o ich istnieniu). W Pythonie programy mają dostęp do własnych drzew parsowania w czasie wykonywania, jeśli ich potrzebują. Makra (statyczne przetwarzanie wstępne) w ogóle nie działa.
Apalala
-5

Ponieważ Python może również programować w niefunkcjonalnym stylu, co nie jest wystarczające dla purystów FP. Jednak bardziej pragmatyczni koderzy mogą cieszyć się zaletami funkcjonalnego stylu, nie będąc do tego dogmatycznym:

„Funkcjonalny programista brzmi raczej jak średniowieczny mnich, odmawiając sobie przyjemności życia w nadziei, że uczyni go cnotliwym. Dla osób bardziej zainteresowanych korzyściami materialnymi te „zalety” nie są zbyt przekonujące. Funkcjonalni programiści twierdzą, że istnieją ogromne korzyści materialne… [ale] jest to po prostu śmieszne. Gdyby pominięcie przydziałów przyniosło tak ogromne korzyści, programiści FORTRAN robiliby to od dwudziestu lat. Logiczną niemożliwością jest uczynienie języka mocniejszym przez pominięcie funkcji, bez względu na to, jak złe mogą być. ”

- John Hughes, Dlaczego programowanie funkcjonalne ma znaczenie

Mason Wheeler
źródło
6
Zgadzam się. Każdy język bez gotojest okropny.
Anon.
2
@Anon: Albo jego wielki brat, call-with-current-continuation.
Chris Jester-Young
4
Mason, cytowany przez ciebie artykuł mówi dokładnie na odwrót. Twój cytat to tylko fragment kilku akapitów artykułu, który opowiada inną historię. W rzeczywistości, jeśli zacytowałbyś oba akapity jako całość, wyraźnie pokazałyby one inne znaczenie.
Marco Mustapic,
2
@Mason: tak, autor twierdzi, że modularyzacja i leniwa ocena to prawdziwe korzyści. Zwróć jednak uwagę na to, że twój oryginalny cytat był poza kontekstem - wydaje się sugerować, że autor twierdzi coś przeciwko FP, podczas gdy w rzeczywistości zacytowałeś paragraf z artykułu z konkluzją, że FP jest świetny, po prostu nie z mylącego powodu „ mniej znaczy więcej". Tytuł artykułu dość wyraźnie pokazuje intencję autora: „dlaczego programowanie funkcjonalne ma znaczenie” ;-) Z pewnością nie potwierdza twojego twierdzenia, że ​​czystsze języki FP są „uciążliwe”.
Andres F.,
6
@Mason Wheeler: Języki hybrydowe wyprzedzają Python o dłuższą metę. Na przykład Lisp pochodzi z 1959 roku i jest w rzeczywistości językiem wieloparadygmatycznym. W pełni obsługuje podejścia funkcjonalne, podejścia proceduralne i podejścia obiektowe do programowania. Dzięki odpowiednim pakietom makr na górze możesz również dość łatwo programować logikę. Schemat również poprzedza Python (i ten artykuł). Wraca do 1975 roku. Może kiedyś powinieneś rzucić okiem na oś czasu języków programowania .
WŁAŚNIE MOJA poprawna OPINIA,