Pourquoi Python n'est-il pas très bon pour la programmation fonctionnelle? [fermé]

324

J'ai toujours pensé que la programmation fonctionnelle pouvait se faire en Python. Ainsi, j'ai été surpris que Python n'ait pas obtenu beaucoup de mention dans cette question, et quand elle a été mentionnée, elle n'était normalement pas très positive. Cependant, peu de raisons ont été données à cela (le manque de correspondance des modèles et les types de données algébriques ont été mentionnés). Donc ma question est: pourquoi Python n'est-il pas très bon pour la programmation fonctionnelle? Y a-t-il plus de raisons que son manque de correspondance de motifs et de types de données algébriques? Ou ces concepts sont-ils si importants pour la programmation fonctionnelle qu'un langage qui ne les prend pas en charge ne peut être classé que comme langage de programmation fonctionnel de second ordre? (Gardez à l'esprit que mon expérience avec la programmation fonctionnelle est assez limitée.)

David Johnstone
la source
2
2018 - Coconut (un langage de programmation fonctionnel qui se compile en Python) améliore la programmation fonctionnelle en Python. Voir aussi cette série d'articles d'IBM page1 page2 page3
cssyphus

Réponses:

393

La question à laquelle vous faites référence demande quels langages favorisent à la fois l'OO et la programmation fonctionnelle. Python ne favorise pas la programmation fonctionnelle même s'il fonctionne assez bien.

Le meilleur argument contre la programmation fonctionnelle en Python est que les cas d'utilisation impératifs / OO sont soigneusement pris en compte par Guido, contrairement aux cas d'utilisation de la programmation fonctionnelle. Quand j'écris Python impératif, c'est l'un des plus beaux langages que je connaisse. Lorsque j'écris du Python fonctionnel, il devient aussi laid et désagréable que votre langage moyen qui n'a pas de BDFL .

Ce qui ne veut pas dire que c'est mauvais, juste que vous devez travailler plus dur que si vous passiez à un langage qui promeut la programmation fonctionnelle ou passiez à l'écriture OO Python.

Voici les choses fonctionnelles qui me manquent en Python:


  • Aucune correspondance de modèle et aucune récursivité de queue signifie que vos algorithmes de base doivent être écrits impérativement. La récursivité est laide et lente en Python.
  • Une petite bibliothèque de listes et aucun dictionnaire fonctionnel signifie que vous devez écrire beaucoup de choses vous-même.
  • Aucune syntaxe pour le curry ou la composition signifie que le style sans point est à peu près aussi plein de ponctuation que les arguments qui passent explicitement.
  • Les itérateurs au lieu des listes paresseuses signifient que vous devez savoir si vous voulez l'efficacité ou la persistance, et disperser les appels listsi vous voulez la persistance. (Les itérateurs sont à usage unique)
  • La syntaxe impérative simple de Python, avec son analyseur LL1 simple, signifie qu'une meilleure syntaxe pour les expressions if et les expressions lambda est fondamentalement impossible. Guido aime ça comme ça, et je pense qu'il a raison.
Nathan Shively-Sanders
la source
5
+1 pour la récursion de queue manquante - bien que les constructions en boucle l'aient remplacée, c'est toujours quelque chose qui vaut la peine de passer entre Python et Scheme.
new123456
5
Excellente réponse quant à l'exhaustivité et la composition. Hélas, comme avec tant de réponses avec un fond fonctionnel solide, il a une utilisation abusive de la terminologie par l'OMI. Bien que je comprenne que vous ne pouvez pas élaborer sur chaque concept dans une réponse, je me demande si l'OP (avec un arrière-plan FP limité admis) est bien informé lors de la lecture de termes tels que "correspondance de modèle", "dictionnaires fonctionnels", "currying" ou " listes paresseuses ".
ThomasH
4
Bon point; Je pense que la solution est d'ajouter des liens. Avez-vous suffisamment de représentants pour modifier ma réponse? Si c'est le cas, n'hésitez pas à ajouter des liens vers les différents concepts. Je commencerai quand j'aurai le temps plus tard.
Nathan Shively-Sanders
5
Je me rends compte que cela a 5 ans, mais… il semble que ce soit plus à propos des choses que vous manquez de Haskell , pas des langages fonctionnels . Par exemple, la plupart des dialectes et descendants ML et Lisp n'ont pas de curry automatique, rendent le style sans point trop verbeux, n'ont pas de listes paresseuses, etc. Donc, si les itérateurs au lieu des listes paresseuses font de Python un mauvais langage fonctionnel, ayant ni faire de CaML un langage fonctionnel terrible ?
abarnert
4
@abarnert: Caml a tous les points, à l'exception des listes paresseuses, qui sont disponibles sous forme de bibliothèque. J'ai parfois utilisé Caml au moment où j'ai écrit cette réponse et j'utilise actuellement F #. Ce sont deux langages fonctionnels très agréables.
Nathan Shively-Sanders
102

Guido en a une bonne explication ici . Voici la partie la plus pertinente:

Je n'ai jamais considéré Python comme fortement influencé par les langages fonctionnels, peu importe ce que les gens disent ou pensent. J'étais beaucoup plus familier avec les langages impératifs tels que C et Algol 68 et même si j'avais fait des fonctions des objets de première classe, je ne voyais pas Python comme un langage de programmation fonctionnel. Cependant, plus tôt, il était clair que les utilisateurs voulaient faire beaucoup plus avec les listes et les fonctions.

...

Il convient également de noter que même si je n'envisageais pas Python comme un langage fonctionnel, l'introduction de fermetures a été utile dans le développement de nombreuses autres fonctionnalités de programmation avancées. Par exemple, certains aspects des classes de style nouveau, des décorateurs et d'autres fonctionnalités modernes reposent sur cette capacité.

Enfin, même si un certain nombre de fonctionnalités de programmation fonctionnelle ont été introduites au fil des ans, Python manque encore de certaines fonctionnalités trouvées dans les «vrais» langages de programmation fonctionnels. Par exemple, Python n'effectue pas certains types d'optimisations (par exemple, la récursivité de queue). En général, en raison de la nature extrêmement dynamique de Python, il est impossible de faire le type d'optimisation au moment de la compilation connu des langages fonctionnels comme Haskell ou ML. Et ça va.

J'en retire deux choses:

  1. Le créateur du langage ne considère pas vraiment Python comme un langage fonctionnel. Par conséquent, il est possible de voir des fonctionnalités «fonctionnelles», mais il est peu probable que vous voyiez quoi que ce soit qui soit définitivement fonctionnel.
  2. La nature dynamique de Python inhibe certaines des optimisations que vous voyez dans d'autres langages fonctionnels. Certes, Lisp est tout aussi dynamique (sinon plus dynamique) que Python, ce n'est donc qu'une explication partielle.
Jason Baker
la source
8
Vous pouvez très bien faire l'optimisation des appels de queue en Python. Guido ne comprend pas / n'a pas compris cela.
Jules
26
Il semble que cela se résume à ce que Guido van Rossum n'aime pas le style fonctionnel.
Svante
59
Je pense qu'il est plus exact de dire que Guido van Rossum ne comprend pas le style fonctionnel et ne comprend pas pourquoi Python en a besoin. Vous devez comprendre deux choses: 1) les langages de programmation sont au bas d'une pile technologique et affectent tout ce qui en découle et 2) comme tout autre logiciel, il est plus facile d'ajouter des fonctionnalités que de les supprimer. Je pense donc que c'est une bonne qualité pour un concepteur de langage de critiquer ce genre de demandes.
Jason Baker
8
"Certes, Lisp est tout aussi dynamique" -> et tout aussi impératif!
pyon
6
@Jules, ça vous dérange de partager une directive pour l'utilisation de l'optimisation des appels de queue en Python? Un pointeur vers une source serait utile.
David Shaked du
52

Le schéma n'a pas de types de données algébriques ou de correspondance de modèles, mais c'est certainement un langage fonctionnel. Choses ennuyeuses sur Python du point de vue de la programmation fonctionnelle:

  1. Lambdas paralysés. Étant donné que Lambdas ne peut contenir qu'une expression et que vous ne pouvez pas tout faire aussi facilement dans un contexte d'expression, cela signifie que les fonctions que vous pouvez définir "à la volée" sont limitées.

  2. Les ifs sont des instructions et non des expressions. Cela signifie, entre autres, que vous ne pouvez pas avoir un lambda avec un If à l'intérieur. (Ceci est corrigé par les ternaires dans Python 2.5, mais il semble laid.)

  3. Guido menace de supprimer la carte, de filtrer et de réduire de temps en temps

D'autre part, python a des fermetures lexicales, Lambdas et des listes de compréhension (qui sont vraiment un concept "fonctionnel" que Guido l'admette ou non). Je fais beaucoup de programmation de "style fonctionnel" en Python, mais je dirais à peine que c'est idéal.

Jacob B
la source
3
Mapper, filtrer et réduire ne sont vraiment pas nécessaires en python. Je n'ai pas encore vu un morceau de code qui a été beaucoup simplifié en les utilisant. De plus, appeler des fonctions en Python peut être coûteux, il est donc généralement préférable d'utiliser simplement une compréhension de liste / générateur ou une boucle for.
Jason Baker
2
C'est exactement ce que Nathan Sanders dit ci-dessous: "Python ne promeut pas la programmation fonctionnelle même s'il fonctionne assez bien." Si Guido voulait que Python devienne un langage fonctionnel, il ferait en sorte que l'implémentation fonctionne suffisamment bien pour utiliser des fonctions jetables, et désengorgerait Lambdas dans la mesure où vous pouvez réellement utiliser map / filter / réduire de manière utile. D'un autre côté, les personnes fonctionnelles commencent à s'éveiller à l'impressionnante compréhension des listes. J'espère que nous n'aurons pas à choisir l'un ou l'autre.
Jacob B
7
la carte et le filtre sont trivialement remplacés par une compréhension de liste. la réduire - presque toujours - si inefficace qu'elle devrait être remplacée par une fonction de générateur.
S.Lott
13
@ S.Lott comment remplacez-vous réduire par un générateur?
Antimony
17
Les compréhensions @JacobB List étaient disponibles dans les langages fonctionnels environ 15 ans avant l'invention de Python et 25 ans avant que Python n'ait acquis une implémentation de la fonctionnalité. L'idée que Python a influencé leur propagation, ou que fp l'a appris de Python, ou même simplement que sa popularité dans le monde fp est postérieure à l'implémentation de Python, est tout simplement fausse. L'implémentation de Python a été tirée directement de Haskell. Peut-être que je vous ai mal compris et que ce n'est pas ce que vous vouliez dire, mais je suis perplexe devant "les gens fonctionnels commencent à se réveiller à l'impressionnant de la compréhension des listes".
itsbruce
23

Je n'appellerais jamais Python «fonctionnel» mais chaque fois que je programme en Python, le code finit toujours par être presque purement fonctionnel.

Certes, cela est principalement dû à la compréhension extrêmement agréable de la liste. Donc, je ne suggérerais pas nécessairement Python comme langage de programmation fonctionnel, mais je suggérerais une programmation fonctionnelle pour toute personne utilisant Python.

Konrad Rudolph
la source
17

Permettez-moi de démontrer avec un morceau de code tiré d'une réponse à une question Python "fonctionnelle" sur SO

Python:

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

La principale différence ici est que la bibliothèque standard de Haskell a des fonctions utiles pour la programmation fonctionnelle: dans ce cas iterate, concatet(!!)

yairchu
la source
7
Voici un remplacement d' une ligne pour le grandKids()corps avec des expressions du générateur: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki
6
Et voici celui qui n'a pas besoin non concatplus:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki
9
@Lloeki: itérer simplifierait considérablement ce code, et (x pour v dans un pour x dans kidsFunc (v)) est beaucoup plus clair que concatMap (kidsFunc). Le manque de Python de belles commandes intégrées d'ordre supérieur rend le code équivalent cryptique et verbeux par rapport à Haskell.
Phob
2
concat peut être remplacé paritertools.chain.from_iterable
Antimony
@Antimony: bon à savoir. thx
yairchu
14

Une chose qui est vraiment importante pour cette question (et les réponses) est la suivante: qu'est-ce que l'enfer est la programmation fonctionnelle, et quelles sont les propriétés les plus importantes de celle-ci. Je vais essayer de vous donner mon avis:

La programmation fonctionnelle ressemble beaucoup à l'écriture de mathématiques sur un tableau blanc. Lorsque vous écrivez des équations sur un tableau blanc, vous ne pensez pas à un ordre d'exécution. Il n'y a (généralement) aucune mutation. Vous ne revenez pas le lendemain et regardez-le, et lorsque vous refaites les calculs, vous obtenez un résultat différent (ou vous pouvez, si vous avez bu du café frais :)). Fondamentalement, ce qui est au tableau est là, et la réponse était déjà là lorsque vous avez commencé à écrire des choses, vous n'avez tout simplement pas encore réalisé ce que c'était.

La programmation fonctionnelle ressemble beaucoup à cela; vous ne changez pas les choses, vous évaluez simplement l'équation (ou dans ce cas, "programme") et déterminez quelle est la réponse. Le programme est toujours là, non modifié. La même chose avec les données.

Je classerais les éléments suivants comme les caractéristiques les plus importantes de la programmation fonctionnelle: a) transparence référentielle - si vous évaluez la même déclaration à un autre moment et à un autre endroit, mais avec les mêmes valeurs de variable, cela signifiera toujours la même chose. b) aucun effet secondaire - peu importe combien de temps vous regardez le tableau blanc, l'équation qu'un autre gars regarde sur un autre tableau blanc ne changera pas accidentellement. c) les fonctions sont aussi des valeurs. qui peuvent être transmises et appliquées avec ou à d'autres variables. d) composition de la fonction, vous pouvez faire h = g · f et ainsi définir une nouvelle fonction h (..) qui équivaut à appeler g (f (..)).

Cette liste est dans mon ordre de priorité, donc la transparence référentielle est la plus importante, suivie par aucun effet secondaire.

Maintenant, si vous passez par python et vérifiez à quel point le langage et les bibliothèques prennent en charge et garantissent ces aspects - alors vous êtes en bonne voie pour répondre à votre propre question.

xeno
la source
2
Les fonctions sont de première classe en Python.
Carl Smith
@CarlSmith Celui-là, mais laisse 3/4 que Python n'a pas. : - \
arya
1
Je ne pense pas que Python soit un bon langage pour la programmation fonctionnelle. Pour être honnête, je ne suis même plus sûr de ce que je voulais dire par ce commentaire. Cela ne semble pas pertinent pour la réponse. Je le supprimerais, mais votre commentaire serait hors contexte.
Carl Smith
1
La transparence référentielle et l'immuabilité ne sont pas vraiment des fonctionnalités linguistiques. Oui, certaines langues (Haskell) les mettent en valeur et rendent difficile de ne pas les avoir, mais vous pouvez créer une fonction référentiellement transparente ou un objet immuable dans pratiquement n'importe quelle langue. Il vous suffit de contourner la bibliothèque standard, qui les violera souvent.
Kevin
En outre, Python prend en charge à la fois le curry et la composition, mais pas au niveau du langage, il se trouve à la place dans la bibliothèque standard.
Kevin
10

Python est presque un langage fonctionnel. C'est "lite fonctionnel".

Il a des fonctionnalités supplémentaires, il n'est donc pas assez pur pour certains.

Il manque également certaines fonctionnalités, il n'est donc pas assez complet pour certains.

Les fonctionnalités manquantes sont relativement faciles à écrire. Découvrez des articles comme celui-ci sur FP en Python.

S.Lott
la source
2
Pour la plupart, je suis d'accord avec ce post. Mais je ne suis pas d'accord pour dire que Python est un langage fonctionnel. Cela encourage beaucoup la programmation impérative, et il est difficile de ne pas le faire avec les "fonctionnalités supplémentaires" que vous mentionnez. Je pense qu'il vaut mieux se référer à Python comme "fonctionnel lite" comme vous l'avez fait dans l'autre post. :-)
Jason Baker
8
-1 Désolé, non. Je veux dire, non. Les langages fonctionnels fournissent des constructions qui facilitent le raisonnement formel: inductif (ML), équationnel (Haskell). Les fermetures et les fonctions anonymes seules ne sont que du sucre syntaxique pour le modèle de stratégie.
pyon
8

Une autre raison non mentionnée ci-dessus est que de nombreuses fonctions et méthodes intégrées de types intégrés modifient un objet mais ne renvoient pas l'objet modifié. Si ces objets modifiés étaient retournés, cela rendrait le code fonctionnel plus propre et plus concis. Par exemple, si some_list.append (some_object) a renvoyé some_list avec some_object en annexe.

DVD Avins
la source
4

En plus d'autres réponses, une des raisons pour lesquelles Python et la plupart des autres langages multi-paradigmes ne sont pas bien adaptés à une véritable programmation fonctionnelle est que leurs compilateurs / machines virtuelles / run-time ne prennent pas en charge l'optimisation fonctionnelle. Ce type d'optimisation est réalisé par le compilateur qui comprend les règles mathématiques. Par exemple, de nombreux langages de programmation prennent en charge unmap fonction ou une méthode. Il s'agit d'une fonction assez standard qui prend une fonction comme un argument et un itérable comme deuxième argument applique ensuite cette fonction à chaque élément de l'itérable.

Quoi qu'il en soit, c'est map( foo() , x ) * map( foo(), y )la même chose quemap( foo(), x * y ) . Ce dernier cas est en fait plus rapide que le premier car le premier effectue deux copies alors que le second en effectue une.

De meilleurs langages fonctionnels reconnaissent ces relations mathématiques et effectuent automatiquement l'optimisation. Les langages qui ne sont pas dédiés au paradigme fonctionnel ne seront probablement pas optimisés.


la source
map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )n'est pas vrai pour toutes les fonctions. Par exemple, considérons le cas lors du foocalcul d'une dérivée.
Eli Korvigo
1
Je pense qu'il voulait dire +au lieu de *.
user1747134
Vous supposez que foo () est linéaire?
juan Isaza
cette propriété n'est PAS vraie pour foo (x) = x + 1. Comme (x + 1) * (y + 1)! = X * y + 1.
juan Isaza