Dlaczego Python nie jest zbyt dobry do programowania funkcjonalnego? [Zamknięte]


324

Zawsze myślałem, że programowanie funkcjonalne można wykonać w języku Python. Byłem więc zaskoczony, że Python nie otrzymał wiele wzmianek w tym pytaniu, a kiedy zostało wspomniane, zwykle nie było zbyt pozytywne. Podano jednak niewiele przyczyn (wspomniano o braku dopasowania wzorca i algebraicznych typach danych). Więc moje pytanie brzmi: dlaczego Python nie jest zbyt dobry do programowania funkcjonalnego? Czy istnieje więcej powodów niż brak dopasowania wzorca i algebraiczne typy danych? Czy te koncepcje są tak ważne dla programowania funkcjonalnego, że język, który ich nie obsługuje, może być sklasyfikowany jako funkcjonalny język programowania drugiej klasy? (Należy pamiętać, że moje doświadczenie z programowaniem funkcjonalnym jest dość ograniczone).


2
2018 - Coconut (funkcjonalny język programowania, który kompiluje się do Pythona) poprawia funkcjonalne programowanie w Pythonie. Zobacz także tę serię artykułów z IBM Strona1 Strona2 Strona3
cssyphus

Odpowiedzi:


393

Pytanie, do którego się odwołujesz, dotyczy tego, które języki promują zarówno program OO, jak i programowanie funkcjonalne. Python nie promuje programowania funkcjonalnego, chociaż działa dość dobrze.

Najlepszym argumentem przeciwko programowaniu funkcjonalnemu w Pythonie jest to, że przypadki użycia imperative / OO są uważnie rozważane przez Guido, podczas gdy przypadki użycia programowania funkcjonalnego nie są. Kiedy piszę imperatywny Python, jest to jeden z najładniejszych języków, jakie znam. Kiedy piszę funkcjonalnego Pythona, staje się on tak brzydki i nieprzyjemny, jak przeciętny język, który nie ma BDFL .

Nie znaczy to, że jest źle, wystarczy, że będziesz musiał pracować ciężej, niż gdybyś przeszedł na język, który promuje funkcjonalne programowanie lub na pisanie OO Python.

Oto funkcjonalne rzeczy, za którymi tęsknię w Pythonie:


  • Brak dopasowania wzorca i brak rekurencji ogona oznacza, że ​​podstawowe algorytmy muszą zostać zapisane bezwzględnie. Rekurencja jest brzydka i powolna w Pythonie.
  • Mała biblioteka list i brak funkcjonalnych słowników oznacza, że ​​musisz sam napisać wiele rzeczy.
  • Brak składni curry lub kompozycji oznacza, że ​​styl bez punktów jest tak samo pełen interpunkcji, jak jawnie przekazywane argumenty.
  • Iteratory zamiast leniwych list oznaczają, że musisz wiedzieć, czy chcesz wydajności, czy wytrwałości, i rozpraszać połączenia, listjeśli chcesz wytrwałości. (Iteratory są jednorazowe)
  • Prosta składnia imperatywna Pythona wraz z prostym parserem LL1 oznaczają, że lepsza składnia wyrażeń if i lambda jest w zasadzie niemożliwa. Guido lubi to w ten sposób i myślę, że ma rację.

5
+1 za brak rekurencji za ogonem - mimo że zapętlone konstrukcje go zastąpiły, nadal warto coś pominąć między Pythonem a schematem.
new123456

5
Doskonała odpowiedź pod względem kompletności i składu. Niestety, podobnie jak w przypadku wielu odpowiedzi z silnym zapleczem funkcjonalnym, IMO obraźliwie używa terminologii. Rozumiem, że w odpowiedzi nie można opracowywać poszczególnych koncepcji, ale zastanawiam się, czy OP (z przyznanym ograniczonym tłem FP) jest dobrze poinformowany podczas czytania terminów takich jak „dopasowywanie wzorców”, „słowniki funkcjonalne”, „curry” lub „ leniwe listy ”.
ThomasH

4
Słuszna uwaga; Myślę, że rozwiązaniem jest dodanie linków. Czy masz wystarczającą liczbę przedstawicieli, aby edytować moją odpowiedź? Jeśli tak, możesz dodać linki do różnych koncepcji. Zacznę, kiedy będę miał później czas.
Nathan Shively-Sanders

5
Zdaję sobie sprawę, że ma to 5 lat, ale… wydaje się, że chodzi bardziej o to, czego brakuje w Haskell , a nie o funkcjonalne języki . Na przykład większość dialektów i potomków ML i Lisp nie ma automatycznego curry, robi nadmiernie gadatliwy styl bez punktów, nie ma leniwych list itp. Tak więc, jeśli iteratory zamiast leniwych list sprawiają, że Python jest złym językiem funkcjonalnym, ponieważ nie może też sprawić, by CaML był okropnym językiem funkcjonalnym?
abarnert

4
@abarnert: Caml ma każdy punktor z wyjątkiem leniwych list, które są dostępne jako biblioteka. Od czasu do czasu korzystałem z Camla, kiedy pisałem tę odpowiedź i obecnie używam F #. Oba są bardzo fajnymi językami funkcjonalnymi.
Nathan Shively-Sanders

102

Guido ma dobre wyjaśnienie tego tutaj . Oto najbardziej odpowiednia część:

Nigdy nie uważałem, że język Python jest pod silnym wpływem języków funkcjonalnych, bez względu na to, co ludzie mówią lub myślą. Byłem znacznie bardziej obeznany z imperatywnymi językami, takimi jak C i Algol 68 i chociaż stworzyłem funkcje obiektów pierwszej klasy, nie widziałem Pythona jako funkcjonalnego języka programowania. Jednak wcześniej było jasne, że użytkownicy chcą zrobić znacznie więcej dzięki listom i funkcjom.

...

Warto również zauważyć, że chociaż nie wyobrażałem sobie Pythona jako języka funkcjonalnego, wprowadzenie zamknięć było przydatne w rozwoju wielu innych zaawansowanych funkcji programowania. Na przykład niektóre aspekty klas w nowym stylu, dekoratorów i innych nowoczesnych funkcji opierają się na tej możliwości.

Wreszcie, mimo że na przestrzeni lat wprowadzono wiele funkcjonalnych funkcji programowania, Python wciąż nie ma pewnych funkcji znalezionych w „prawdziwych” funkcjonalnych językach programowania. Na przykład Python nie wykonuje pewnych rodzajów optymalizacji (np. Rekurencji ogona). Ogólnie rzecz biorąc, ponieważ niezwykle dynamiczna natura Pythona nie jest możliwa do przeprowadzenia w czasie optymalizacji znanym z języków funkcjonalnych, takich jak Haskell lub ML. I w porządku.

Wyciągam z tego dwie rzeczy:

  1. Twórca języka tak naprawdę nie uważa Pythona za język funkcjonalny. Dlatego można zobaczyć funkcje „funkcjonalne”, ale jest mało prawdopodobne, aby zobaczyć coś, co jest definitywnie funkcjonalne.
  2. Dynamiczna natura Pythona hamuje niektóre optymalizacje widoczne w innych językach funkcjonalnych. To prawda, że ​​Lisp jest tak samo dynamiczny (jeśli nie bardziej dynamiczny) jak Python, więc jest to tylko częściowe wyjaśnienie.

8
Możesz zrobić optymalizację połączeń ogonowych w Pythonie. Guido tego nie rozumiał / nie rozumiał.
Jules

26
Wydaje się, że sprowadza się to do tego, że Guido van Rossum nie lubi funkcjonalnego stylu.
Svante

59
Myślę, że dokładniej jest powiedzieć, że Guido van Rossum nie rozumie funkcjonalnego stylu i nie rozumie, dlaczego Python ich potrzebuje. Musisz zrozumieć dwie rzeczy: 1) języki programowania znajdują się na dole stosu technologii i wpływają na wszystko, co na nich zbudowane, i 2) tak jak każde inne oprogramowanie, łatwiej jest dodawać funkcje niż je usuwać. Myślę więc, że to dobra jakość dla projektantów językowych, by krytycznie reagować na tego rodzaju prośby.
Jason Baker

8
„To prawda, Lisp jest tak samo dynamiczny” -> i równie konieczny!
pyon

6
@Jules, czy masz coś przeciwko podzieleniu się wytycznymi dotyczącymi optymalizacji optymalizacji połączeń w Pythonie? Przydałby się wskaźnik do jakiegoś źródła.
David Shaked

52

Schemat nie ma algebraicznych typów danych ani dopasowywania wzorców, ale z pewnością jest to język funkcjonalny. Irytujące rzeczy dotyczące Pythona z funkcjonalnego punktu widzenia programowania:

  1. Crippled Lambdas. Ponieważ Lambdas może zawierać tylko wyrażenie i nie możesz zrobić wszystkiego tak łatwo w kontekście wyrażenia, oznacza to, że funkcje, które możesz zdefiniować „w locie” są ograniczone.

  2. Jeśli są wyrażeniami, a nie wyrażeniami. Oznacza to, między innymi, że nie możesz mieć lambda z If w środku. (Jest to naprawione przez trójskładniki w Pythonie 2.5, ale wygląda brzydko.)

  3. Guido grozi usunąć mapę, filtr i zmniejszyć raz na jakiś czas

Z drugiej strony, python ma leksykalne zamknięcia, Lambdas i wyrażenia listowe (które są tak naprawdę „funkcjonalną” koncepcją, niezależnie od tego, czy Guido to dopuszcza). W Pythonie dużo programuję w stylu „funkcjonalnym”, ale nie powiedziałbym, że jest idealny.


3
Mapowanie, filtrowanie i zmniejszanie naprawdę nie są potrzebne w Pythonie. Nie widziałem jeszcze kodu, który został bardzo uproszczony dzięki ich użyciu. Poza tym wywoływanie funkcji w Pythonie może być kosztowne, dlatego zwykle lepiej jest po prostu korzystać ze zrozumienia listy / generatora lub pętli for.
Jason Baker

2
Dokładnie tak mówi Nathan Sanders: „Python nie promuje programowania funkcjonalnego, nawet jeśli działa dość dobrze”. Gdyby Guido chciał, aby Python stał się językiem funkcjonalnym, sprawiłby, że implementacja działałaby wystarczająco dobrze, by używać funkcji odrzucania, i zepsułby Lambdas do tego stopnia, że ​​faktycznie można użyć mapy / filtru / redukcji w użyteczny sposób. Z drugiej strony funkcjonalni ludzie zaczynają budzić się z powodu niesamowitości list. Mamy nadzieję, że nie będziemy musieli wybierać jednego lub drugiego.
Jacob B

7
Mapa i filtr są w trywialny sposób zastąpione listą. zmniejsz - prawie zawsze - jest tak nieefektywny, że należy go zastąpić funkcją generatora.
S.Lott,

13
@ S.Lott, jak zastąpić redukcję generatorem?
Antimony

17
@JacobB Listy były dostępne w językach funkcjonalnych około 15 lat przed wynalezieniem Pythona i 25 lat przed tym, jak Python uzyskał implementację tej funkcji. Pomysł, że Python wpłynął na ich rozprzestrzenianie się lub że fp nauczył się tego od Pythona, a nawet po prostu, że jego popularność w świecie fp po wdrożeniu Pythona jest po prostu błędna. Implementacja Pythona została zaczerpnięta bezpośrednio z Haskell. Może źle cię zrozumiałem i nie o to ci chodziło, ale jestem zakłopotany „funkcjonalnymi ludźmi zaczynają budzić się z powodu niesamowitości list”.
itsbruce

23

Nigdy nie nazwałbym Pythona „funkcjonalnym”, ale za każdym razem, gdy programuję w Pythonie, kod niezmiennie kończy się prawie wyłącznie funkcjonalnym.

Trzeba przyznać, że wynika to głównie z wyjątkowo miłego zrozumienia listy. Niekoniecznie więc sugerowałbym Python jako funkcjonalny język programowania, ale sugerowałbym programowanie funkcjonalne dla każdego, kto używa Pythona.


17

Pokażę kawałek kodu zaczerpnięty z odpowiedzi na „funkcjonalne” pytanie Pythona dotyczące SO

Pyton:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

Główną różnicą jest to, że biblioteka standardowa Haskell zawiera użyteczne funkcje programowania funkcyjnego: w tym przypadku iterate, concati(!!)


7
Oto wymiana jedna linia do grandKids()ciała z generatora wyrażeń: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki

6
A oto jedna, która nie potrzebuje concatteż:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki,

9
@Lloeki: iteracja znacznie uprościłaby ten kod, a (x dla v w dla x dla kidsFunc (v)) jest o wiele jaśniejsze niż concatMap (kidsFunc). Brak ładnych wbudowanych funkcji wyższego rzędu w Pythonie sprawia, że ​​równoważny kod jest tajemniczy i pełny w porównaniu z Haskellem.
Phob

2
concat można zastąpić przezitertools.chain.from_iterable
Antimony

@Antimony: dobrze wiedzieć. thx
yairchu

14

Jedną z rzeczy, która jest naprawdę ważna dla tego pytania (i odpowiedzi) jest: Co to do cholery jest funkcjonalne programowanie i jakie są najważniejsze jego właściwości. Spróbuję przedstawić mój pogląd na to:

Programowanie funkcjonalne przypomina pisanie matematyki na tablicy. Kiedy piszesz równania na tablicy, nie myślisz o poleceniu wykonania. Nie ma (zazwyczaj) mutacji. Nie wrócisz pojutrze i nie spojrzysz na to, a kiedy ponownie wykonasz obliczenia, uzyskasz inny wynik (a może, jeśli napijesz się świeżej kawy :)). Zasadniczo to, co jest na tablicy, a odpowiedź już tam była, kiedy zacząłeś spisywać rzeczy, po prostu nie zdawałeś sobie sprawy, co to jest.

Programowanie funkcjonalne jest bardzo podobne; nie zmieniasz rzeczy, po prostu oceniasz równanie (lub w tym przypadku „program”) i zastanawiasz się, jaka jest odpowiedź. Program jest nadal niezmodyfikowany. To samo z danymi.

Za najważniejsze cechy programowania funkcjonalnego uznałbym: a) przejrzystość referencyjną - jeśli ocenisz to samo zdanie w innym miejscu i czasie, ale przy tych samych wartościach zmiennych, będzie to nadal oznaczać to samo. b) brak efektu ubocznego - bez względu na to, jak długo patrzysz na tablicę, równanie, na które patrzy inny facet, nie zmieni się przypadkowo. c) funkcje też są wartościami. które można przekazywać i stosować z innymi zmiennymi lub do nich. d) składanie funkcji, możesz zrobić h = g · f, a tym samym zdefiniować nową funkcję h (..), która jest równoważna wywołaniu g (f (..)).

Ta lista jest w mojej kolejności według priorytetów, więc przejrzystość referencyjna jest najważniejsza, a po niej nie występują żadne skutki uboczne.

Teraz, jeśli przejdziesz przez python i sprawdzisz, jak dobrze język i biblioteki obsługują i gwarantują te aspekty - jesteś na dobrej drodze, aby odpowiedzieć na swoje własne pytanie.


2
Funkcje są najwyższej klasy w Pythonie.
Carl Smith

@CarlSmith Ten, ale pozostawia 3/4, którego Python nie ma. : - \
arya

1
Nie sądzę, aby Python był dobrym językiem do programowania funkcjonalnego. Nie jestem nawet teraz pewien, co miałem na myśli przez ten komentarz, mówiąc szczerze. To nie wydaje się istotne dla odpowiedzi. Chciałbym go usunąć, ale wtedy twój komentarz byłby poza kontekstem.
Carl Smith

1
Referencyjna przejrzystość i niezmienność nie są tak naprawdę funkcjami językowymi. Tak, niektóre języki (Haskell) podkreślają je i utrudniają ich nieobecność, ale można utworzyć referencyjnie przezroczystą funkcję lub niezmienny obiekt w zasadniczo dowolnym języku. Musisz po prostu obejść standardową bibliotekę, która często je narusza.
Kevin

Ponadto Python obsługuje zarówno curry, jak i kompozycję, ale nie na poziomie języka, zamiast tego jest w standardowej bibliotece.
Kevin

10

Python jest prawie językiem funkcjonalnym. To „funkcjonalna lite”.

Ma dodatkowe funkcje, więc dla niektórych nie jest wystarczająco czysty.

Brakuje również niektórych funkcji, więc dla niektórych nie jest wystarczająco kompletny.

Brakujące funkcje są stosunkowo łatwe do napisania. Sprawdź posty jak ten na FP w Pythonie.


2
W większości zgadzam się z tym postem. Ale nie mogę się zgodzić z tym, że Python jest językiem funkcjonalnym. To bardzo zachęca do programowania imperatywnego i trudno jest nie wspomnieć o „dodatkowych funkcjach”, o których wspominasz. Myślę, że najlepiej jest nazywać Python „funkcjonalnym lite”, tak jak zrobiłeś to w innym poście. :-)
Jason Baker

8
-1 Przepraszam, nie. Mam na myśli, po prostu nie. Języki funkcjonalne zapewniają konstrukcje ułatwiające formalne rozumowanie: indukcyjny (ML), równy (Haskell). Same zamknięcia i anonimowe funkcje są po prostu cukrem syntaktycznym dla wzorca strategii.
pyon

8

Innym powodem nie wymienionym powyżej jest to, że wiele wbudowanych funkcji i metod typów wbudowanych modyfikuje obiekt, ale nie zwraca zmodyfikowanego obiektu. Gdyby te zmodyfikowane obiekty zostały zwrócone, kod funkcjonalny byłby czystszy i bardziej zwięzły. Na przykład, jeśli some_list.append (some_object) zwrócił some_list z dołączonym some_object.


4

Oprócz innych odpowiedzi, jednym z powodów, dla których Python i większość innych języków z wieloma paradygmatami nie są odpowiednie dla prawdziwego programowania funkcjonalnego, jest to, że ich kompilatory / maszyny wirtualne / czasy działania nie obsługują optymalizacji funkcjonalnej. Tego rodzaju optymalizację osiąga kompilator rozumiejący reguły matematyczne. Na przykład wiele języków programowania obsługuje mapfunkcję lub metodę. Jest to dość standardowa funkcja, która przyjmuje funkcję jako jeden argument, a iterowalną jako drugi argument, a następnie stosuje tę funkcję do każdego elementu w iteracji.

W każdym razie okazuje się, że map( foo() , x ) * map( foo(), y )jest taki sam jak map( foo(), x * y ). Drugi przypadek jest w rzeczywistości szybszy niż pierwszy, ponieważ pierwszy wykonuje dwie kopie, podczas gdy drugi wykonuje jedną.

Lepsze języki funkcjonalne rozpoznają te matematyczne zależności i automatycznie przeprowadzają optymalizację. Języki, które nie są dedykowane do paradygmatu funkcjonalnego, prawdopodobnie się nie zoptymalizują.


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )nie jest prawdziwe dla wszystkich funkcji. Na przykład rozważmy przypadek przy fooobliczaniu pochodnej.
Eli Korvigo,

1
Myślę, że miał na myśli +zamiast *.
user1747134,

Zakładasz, że foo () jest liniowy?
juan Isaza

ta właściwość NIE jest prawdziwa dla foo (x) = x + 1. As (x + 1) * (y + 1)! = X * y + 1.
juan Isaza
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.