Si nous pouvons faire de la programmation fonctionnelle avec Python, avons-nous besoin d'un langage de programmation fonctionnel spécifique? [fermé]

22

En utilisant des générateurs et lambda, nous pouvons faire de la programmation fonctionnelle avec Python. Vous pouvez également réaliser la même chose avec Ruby.

La question est donc: pourquoi avons-nous besoin de langages de programmation fonctionnels spécifiques tels que Erlang, Haskell et Scheme? Y a-t-il quelque chose de différent que ces langages de programmation fonctionnels spécifiques fournissent? Pourquoi ne pouvons-nous pas simplement utiliser Python pour la programmation fonctionnelle?

Joshua Partogi
la source
30
tout cela existait avant même la création de python
Mahmoud Hossam
51
Un gué Pinto est une voiture. Pourquoi avons-nous besoin de voitures rapides spécifiques comme les Ferrari?
Martin York
11
En utilisant des classes et des modèles, nous pouvons faire n'importe quoi OO en C ++. Pourquoi Java et Python ont-ils été créés? Qu'ajoutent-ils?
9000
19
Tous les langages de programmation (sans certains langages de recherche purement académiques) sont équivalents à Turing, donc ce que vous pouvez faire dans le langage A vous pouvez le faire dans n'importe quel autre langage. Donc, suivant ce courant de pensée, nous n'avons besoin que d'un seul langage complet de Turing - disons comme sendmail.cf;) okmij.org/ftp/Computation/#sendmail-Turing
Maglob
17
Si vous connaissiez l'un de ces langages, vous ne diriez pas que Python fait bien la programmation fonctionnelle. Ce n'est pas le cas. Il le fait assez bien pour intégrer une part de choses FP-ish, mais pas mieux.

Réponses:

20

J'apprécie la question, car je suis personnellement un grand fan à la fois du style de programmation Python et fonctionnel. J'ai une longue expérience en Python et j'ai commencé à apprendre Haskell récemment, alors voici quelques points basés sur mes expériences personnelles sur les différences entre ces langues aussi, d'un point de vue fonctionnel.

Pureté

Même si vous ne vous souciez pas de la pureté des fonctions (c'est-à-dire de l'absence d'effets secondaires) en tant que principe, cela a un effet pratique sur la facilité avec laquelle il est possible de lire le code et de le raisonner. Même si vous maintenez la pureté dans vos propres fonctions Python, il y a une grande différence à ce que le compilateur applique la pureté et, surtout, que la bibliothèque standard soit construite en termes de pureté et de structures de données immuables.

Performance

Vous pouvez ou non vous soucier des performances en fonction de votre domaine d'application, mais le typage statique et la pureté garantie donnent beaucoup plus de travail au compilateur, par rapport à Python et à d'autres langages dynamiques (bien que je dois admettre que PyPy fait de grands incursions, et par exemple LuaJIT est proche de miraculeux).

Optimisation des appels de queue

Lié aux performances, mais légèrement différent. Même si vous ne vous souciez pas trop des performances d'exécution, l'absence d'optimisation des appels de queue (en particulier pour la récursivité de queue) limite les façons dont vous pouvez implémenter des algorithmes en Python sans atteindre les limites de la pile.

Syntaxe

C'est la principale raison pour laquelle j'ai commencé à regarder les "vrais" langages fonctionnels au lieu d'utiliser simplement Python avec un style fonctionnel. Bien que je pense que Python a une syntaxe très expressive en général, il a quelques points faibles spécifiques au codage fonctionnel. Par exemple:

  • La syntaxe des fonctions lambda est assez verbeuse et limitée dans ce qu'elles peuvent contenir
  • Pas de sucre syntaxique pour la composition de fonction par exemple par f = g . hrapport àf = lambda *arg: g(h(*arg))
  • Pas de sucre syntaxique pour une application partielle, c.-à-d. f = map gVsf = functools.partial(map, g)
  • Pas de sucre syntaxique pour l' utilisation des opérateurs infixes dans les fonctions d'ordre supérieur -à- dire par sum = reduce (+) lstrapport auxsum = reduce(operator.add, lst)
  • Pas de correspondance de modèle ou de garde pour les arguments de fonction, ce qui facilite l'expression des conditions de fin de récursivité et certains cas de bordure avec une syntaxe très lisible.
  • Les crochets ne sont jamais facultatifs pour les appels de fonction, et il n'y a pas de sucre syntaxique pour les appels imbriqués. Je suppose que c'est une question de goût, mais surtout dans le code fonctionnel, je trouve qu'il est courant d'enchaîner les appels de fonction et je trouve y = func1 $ func2 $ func3 xplus facile à lire que y = func1(func2(func3(x))), une fois que vous êtes familiarisé avec cette notation.
shang
la source
28

Ce sont les différences les plus importantes:

Haskell

  • Évaluation paresseuse
  • Compile en code machine
  • Le typage statique garantit que les fonctions sont pures
  • Inférence de type

Haskell et Erlang

  • Correspondance de motifs

Erlang

  • Modèle d'acteur de simultanéité, processus légers

Schème

  • Macros

Toutes les langues

  • vraies fermetures (ruby a des fermetures, si le python peut être débattu, voir les commentaires)
  • une bibliothèque standard adaptée à un style de programmation fonctionnel (collections immuables, carte, filtre, pli, etc.)
  • récursivité de la queue (cela peut également être trouvé dans certains langages non fonctionnels)

En outre, vous devriez jeter un œil aux langages de la famille ML comme SML, Ocaml et F # et Scala, qui fusionnent OO et la programmation fonctionnelle d'une nouvelle manière. Toutes ces langues ont des caractéristiques intéressantes uniques.

Kim
la source
3
+1 bon message. Vous pouvez ajouter l'inférence de type sur Haskell et les processus légers sur Erlang.
Jonas
1
Python a une carte, un filtre et un pli (réduction). Concernant les "vraies fermetures": Si vous définissez une vraie fermeture comme une fermeture qui peut contenir autre chose qu'une seule expression, Haskell n'a pas non plus de vraies fermetures (mais bien sûr Haskell a peu de choses qui ne sont pas des expressions ...) . Cependant, le point sur les structures de données immuables est bon, donc +1 pour cela. Aussi: récursivité de queue (et récursion généralement moins chère).
sepp2k
2
+1 pour "la saisie statique garantit que les fonctions sont pures". C'est très cool d'avoir un système de type qui fait la distinction entre les fonctions pures et non pures. (Nombre de morve de biche const de C ++. :)
Macke
1
@btilly: Je ne considérerais pas cela comme une fermeture si vous ne pouvez pas attribuer à une variable qui est dans la portée. Sinon, nous devons dire que Java a également des fermetures, car vous pouvez utiliser la même astuce là-bas.
Kim
3
Dans une fermeture, je peux accéder à une variable de la même manière que je le fais normalement. Cela est vrai des fermetures de Haskell et Ruby, mais pas des pauvres substituts de Python ou Java. Peut-être que quelqu'un d'autre peut nous éclairer sur erlang, je ne le sais pas très bien.
Kim
19

Il est difficile de définir exactement ce qu'est un «langage fonctionnel» - parmi les langages que vous avez énumérés, seul Haskell est purement fonctionnel (tous les autres adoptent une sorte d'approche hybride). Cependant, certaines fonctionnalités de langage sont très utiles pour la programmation fonctionnelle, et Ruby et Python n'en ont pas assez pour être de très bons environnements pour FP. Voici ma liste de contrôle personnelle, par ordre d'importance:

  1. Fonctions et fermetures de première classe (Ruby, Python et tous les autres que vous avez répertoriés en ont).
  2. Optimisation des appels de queue garantie (Erlang, Haskell, Scala et Scheme ont cela, mais pas Python, Ruby ou Clojure (encore)).
  3. Prise en charge de l' immuabilité dans le langage et les bibliothèques standard (c'est un grand que tous les "langages fonctionnels" que vous avez répertoriés (sauf Scheme) mais Ruby et Python n'en ont pas).
  4. Prise en charge au niveau du langage pour les fonctions référentiellement transparentes (ou pures) (pour autant que je sache, seul Haskell en dispose actuellement).

La nécessité de (1) devrait être évidente - les fonctions d'ordre supérieur sont extrêmement difficiles sans fonctions de première classe. Lorsque les gens disent que Ruby et Python sont de bons langages pour la PF, ils en parlent généralement. Cependant, cette caractéristique particulière est nécessaire mais pas suffisante pour rendre un langage bon pour la PF.

(2) est une nécessité traditionnelle pour la PF depuis que Scheme a été inventé. Sans TCO, il est impossible de programmer avec une récursion profonde, qui est l'une des pierres angulaires de FP, car vous obtenez des débordements de pile. Le seul langage "fonctionnel" (par définition populaire) qui n'en dispose pas est Clojure (en raison des limitations de la JVM), mais Clojure a une variété de hacks pour simuler le TCO. (Pour info, Ruby TCO est spécifique à l'implémentation , mais Python ne le prend pas spécifiquement en charge .) La raison pour laquelle le TCO doit être garanti est que s'il est spécifique à l'implémentation, les fonctions récursives profondes vont rompre avec certaines implémentations, donc vous ne pouvez pas vraiment les utiliser du tout.

(3) est une autre grande chose que les langages fonctionnels modernes (en particulier Haskell, Erlang, Clojure et Scala) ont que Ruby et Python n'ont pas. Sans entrer dans trop de détails, l'immuabilité garantie élimine des classes entières de bogues, en particulier dans des situations simultanées, et permet des choses soignées comme des structures de données persistantes . Il est très difficile de profiter de ces avantages sans un support au niveau de la langue.

(4) est, pour moi, la chose la plus intéressante à propos des langages purement fonctionnels (par opposition aux langages hybrides). Considérez la fonction Ruby extrêmement simple suivante:

def add(a, b)
  a + b
end

Cela ressemble à une fonction pure, mais en raison de la surcharge de l'opérateur, elle peut muter l'un des paramètres ou provoquer des effets secondaires tels que l'impression sur la console. Il est peu probable que quelqu'un surcharge l' +opérateur pour avoir un effet secondaire, mais le langage ne donne aucune garantie. (La même chose s'applique à Python, mais peut-être pas avec cet exemple spécifique.)

Dans un langage purement fonctionnel, en revanche, il existe des garanties au niveau du langage que les fonctions sont référentiellement transparentes. Cela présente de nombreux avantages: les fonctions pures peuvent être facilement mémorisées; ils peuvent être facilement testés sans dépendre d'aucune sorte d'état global; et les valeurs de la fonction peuvent être évaluées paresseusement ou en parallèle sans se soucier des problèmes de concurrence. Haskell en profite pleinement, mais je ne connais pas suffisamment les autres langages fonctionnels pour savoir s'ils le font.

Cela étant dit, il est possible d'utiliser les techniques de FP dans presque tous les langages (même Java). Par exemple, MapReduce de Google est inspiré par des idées fonctionnelles, mais pour autant que je sache, ils n'utilisent aucun langage "fonctionnel" pour leurs grands projets (je pense qu'ils utilisent principalement C ++, Java et Python).

shosti
la source
2
+1 pour l'explication approfondie - même moi en tant qu'étranger FP l'a compris. Merci! :-)
Péter Török
la réponse la plus valable à cette question jusqu'à présent. Fondé sur des faits et de nombreuses informations. Bon travail.
wirrbel
Scala a une récursivité de queue et, comme avec Scheme, cela se fait automatiquement si un appel récursif est en position de queue (contrairement à Clojure où il doit être explicitement demandé). Il y a même une annotation de sorte que vous pouvez vérifier que le compilateur va générer du code récursif tail. Ce qu'il n'a pas, c'est le TCO plus généralisé de Scheme. Je suppose que vous le savez, mais comme vous êtes entré dans beaucoup de détails sur la plupart des autres choses, cela semblait être un étrange renvoi / omission.
itsbruce
@itsbruce Ce message est assez ancien, IIRC Scala ne l'avait pas à l'époque (ou je me suis peut-être trompé;). Mis à jour.
shosti
Je n'ai pas utilisé Scala depuis le début, mais il y a eu une récursivité de la queue en 2008, quand je m'intéressais ;-) Je renvoie les personnes qui posent des questions sur ce sujet à cette question SO particulière car elle contient de bonnes réponses et je remarqué cette bizarrerie, donc commenté pour être complet.
itsbruce
11

Les langues que vous mentionnez sont très différentes.

Alors que Python et Ruby sont des langages typés dynamiquement, Haskell est typé statiquement. Erlang est une langue simultanée et utilise le modèle Actor et est très différent de toutes les autres langues que vous mentionnez.

Python et Ruby ont de nombreuses constructions impératives alors que dans un langage fonctionnel plus pur comme Haskell, tout renvoie quelque chose ou en d'autres termes tout est une fonction.

Jonas
la source
@kRON: Eh bien, le système de type est une propriété importante d'un langage, et il a demandé "Y a-t-il quelque chose de différent que ces langages de programmation fonctionnels spécifiques fournissent?". Bien sûr, vous pouvez utiliser le modèle Actor avec d'autres langues mais Erlang l'a intégré dans la langue. Erlang utilise des processus légers et possède des constructions de langage intégrées pour la programmation distribuée - de là un langage "simultané".
Jonas
8

En retard à la fête comme d'habitude, mais je vais quand même dire des choses.

Un langage de programmation fonctionnel n'est pas un langage qui permet une programmation fonctionnelle. Si nous devions suivre cette définition, alors pratiquement n'importe quel langage n'importe où est un langage de programmation fonctionnel. (La même chose s'applique à la POO, soit dit en passant. Vous pouvez écrire dans un style de POO en C si vous le souhaitez. Ainsi, selon votre logique, C est un langage de POO.)

Ce qui fait qu'un langage de programmation fonctionnel n'est pas ce qu'il vous permet de programmer comme, c'est ce qu'il vous permet de programmer facilement . Voilà la clé.

Ainsi, Python a des lambdas (qui sont des affaires incroyablement anémiques) et vous offre quelques fonctions de bibliothèque que vous verrez dans les bibliothèques fonctionnelles ainsi que "map" et "fold". Cependant, cela ne suffit pas pour en faire un langage de programmation fonctionnel, car il est difficile, voire impossible, de le programmer de manière cohérente dans un style fonctionnel approprié (et le langage n'applique certainement pas ce style!). À la base, Python est un langage impératif concerné par les opérations d'état et de manipulation d'état, ce qui est tout simplement contraire à l'expression et à la sémantique d'évaluation des expressions d'un langage fonctionnel.

Alors pourquoi avons-nous des langages de programmation fonctionnels quand Python (ou Ruby (ou insérez le langage de votre choix)) peut "faire de la programmation fonctionnelle"? Parce que Python, et al ne peuvent pas faire une programmation fonctionnelle appropriée. Voilà pourquoi.

JUSTE MON AVIS correct
la source
6

Vous pouvez faire de la programmation fonctionnelle en Java (voir par exemple http://functionaljava.org/ ). Vous pouvez aussi faire la programmation orientée objet en C . Ce n'est tout simplement pas si idiomatique.

Donc, en effet, nous n'avons absolument pas besoin d'Erlang, Haskell, Scheme ou d'un langage de programmation spécifique, mais ils représentent tous des approches et des compromis différents, ce qui rend certaines tâches plus faciles et plus difficiles. Ce que vous devez utiliser dépend de ce que vous souhaitez réaliser.

Joonas Pulakka
la source
4

Cette question peut être appliquée à un nombre infini de langues et de paradigmes.

  • Puisque tout le monde utilise C ++, pourquoi avons-nous besoin d'autres langages à usage général?
  • Puisque java est un si grand langage OO, pourquoi existe-t-il d'autres langages OO?
  • Puisque perl est un langage de script incroyable, pourquoi avons-nous besoin de python?
  • Yatta, yatta, yatta

La plupart, sinon toutes les langues existent pour une raison spécifique. Ils existent parce que quelqu'un avait un besoin qu'aucune langue courante ne remplissait ou mal rempli. (Bien sûr, cela ne s'applique pas à toutes les langues, mais je pense que cela s'applique à la plupart des langues bien connues.) Par exemple, python a été initialement développé pour s'interfacer avec le système d'exploitation Amoeba [ 1 , 2 ] et Erlang a été créé pour aider dans le développement d'applications de téléphonie [ 3 ]. Donc une réponse à la question "Pourquoi avons-nous besoin d'un autre langage fonctionnel?" peut simplement l'être, car [insérer-nom-de-quelqu'un-qui-sait-comment-concevoir-les langages] n'a pas aimé la façon dont python l'a fait.

Cela résume à peu près ce que je pense que la réponse est. Alors que vous pouvez faire avec python tout ce que vous pouvez faire avec un langage fonctionnel, le voudriez-vous vraiment? Tout ce que vous pouvez faire en C, vous pouvez le faire en assemblage, mais le voudriez-vous? Différentes langues seront toujours les meilleures pour faire des choses différentes, et c'est ainsi que cela devrait être.

cledoux
la source
2

La programmation fonctionnelle concerne autant un paradigme de conception que des fonctionnalités de langage spécifiques. Ou, autrement dit, lambdas et une fonction de carte ne font pas un langage de programmation fonctionnel. Python et Ruby ont certaines fonctionnalités inspirées des langages de programmation fonctionnels, mais vous écrivez toujours généralement du code de manière très impérative. (C'est un peu comme C: vous pouvez écrire du code de type OO en C, mais personne ne considère sérieusement C comme un langage OO.)

Regardez: la programmation fonctionnelle ne concerne pas seulement les lambdas mapou les fonctions d'ordre supérieur. Il s'agit de design . Un programme écrit dans un "vrai" langage de programmation fonctionnel résout les problèmes via la composition des fonctions. Bien que les programmes écrits en Ruby ou Python puissent utiliser des fonctionnalités de type FP, ils ne lisent généralement pas comme un ensemble de fonctions composées.

mipadi
la source
0

Chaque langage fonctionnel que vous avez mentionné s'intègre assez bien dans une certaine niche et les modèles idiomatiques que chacun encourage les rendent très bien adaptés à certaines tâches qui seraient impossibles à accomplir en Python à moins d'avoir construit une énorme bibliothèque de modules d'assistance. Le plus évident d'une telle excellence de niche est le modèle de concurrence d'Erlang. Les autres ont également des forces similaires.

davidk01
la source
0

Chaque concept disponible en lambda-calcul, LISP et Scheme est disponible en Python, donc, oui, vous pouvez y faire une programmation fonctionnelle. Si c'est pratique ou non, c'est une question de contexte et de goût.

Vous pouvez facilement écrire un interpréteur LISP (ou autre langage fonctionnel) en Python (Ruby, Scala) (qu'est-ce que cela signifie?). Vous pouvez écrire un interpréteur pour Python en utilisant purement fonctionnel, mais cela prendrait beaucoup de travail. Même les langages "fonctionnels" sont aujourd'hui multi-paradigmes.

Ce vieux et beau livre contient la plupart (mais pas toutes) des réponses sur l'essence de la programmation fonctionnelle.

Apalala
la source
@Arkaaito Mais êtes-vous d'accord pour dire que tous les concepts disponibles dans le calcul lambda, LISP et Scheme sont disponibles en Python?
Mark C
Êtes-vous sûr que chaque concept lisp est disponible en Python? Macros, code-est-des-données?
SK-logic
@ SK-logic Les macros ne sont pas en lambda-calcul, mais oui, elles sont disponibles en Python, mais pas sous ce nom. Python fournit une eval()fonction, qui est nécessaire pour que le code soit des données , mais cela va plus loin: il vous permet de modifier la plupart de l'environnement d'exécution, comme dans LISP.
Apalala
@Apalala, les langues dynamiques eval()est une métaprogrammation d'exécution. Parfois, c'est utile, mais c'est extrêmement cher. Les macros Lisp sont différentes, c'est une métaprogrammation en temps de compilation. Il n'est disponible en Python sous aucune forme utilisable.
SK-logic
@ SK-logic Ce type de macros brise le principe du code est une donnée , car les programmes ne peuvent pas changer les macros (ou même connaître leur existence). En Python, les programmes ont accès à leurs propres arbres d'analyse au moment de l'exécution, s'ils en ont besoin. Les macros (prétraitement statique) ne fonctionnent pas du tout.
Apalala
-5

Parce que Python peut également programmer dans un style non fonctionnel, et ce n'est pas suffisant pour le puriste FP. Les codeurs plus pragmatiques, cependant, peuvent profiter des avantages du style fonctionnel sans être dogmatique à ce sujet:

«Le programmeur fonctionnel ressemble plutôt à un moine médiéval, se privant des plaisirs de la vie dans l'espoir que cela le rendra vertueux. Pour ceux plus intéressés par les avantages matériels, ces «avantages» ne sont pas très convaincants. Les programmeurs fonctionnels soutiennent qu'il y a de grands avantages matériels… [mais] c'est tout simplement ridicule. Si l'omission des déclarations d'affectation apportait des avantages si énormes, les programmeurs FORTRAN l'auraient fait pendant vingt ans. C'est une impossibilité logique de rendre un langage plus puissant en omettant des fonctionnalités, quelle que soit leur gravité. »

- John Hughes, Pourquoi la programmation fonctionnelle est importante

Mason Wheeler
la source
6
Je suis d'accord. Toute langue sans gotoest terrible.
Anon.
2
@Anon: Ou son grand frère, call-with-current-continuation.
Chris Jester-Young
4
Mason, le papier que vous citez dit exactement le contraire. Votre citation n'est qu'un fragment de quelques paragraphes d'un article qui raconte une histoire différente. En fait, si vous citiez les deux paragraphes dans leur ensemble, ils montreraient clairement une autre signification.
Marco Mustapic
2
@Mason: oui, l'auteur affirme que la modularisation et l'évaluation paresseuse sont les vrais avantages. Mais notez à quel point votre citation originale était hors contexte - vous semblez suggérer que l'auteur réclame quelque chose contre FP, alors qu'en fait vous avez cité un paragraphe d'un article avec la conclusion que FP est génial, mais pas pour la raison trompeuse de " moins est plus". Le titre de l'article montre assez clairement l'intention de l'auteur: "pourquoi la programmation fonctionnelle est importante" ;-) Cela ne supporte certainement pas votre affirmation selon laquelle les langues FP plus pures "sont une nuisance".
Andres F.
6
@Mason Wheeler: les langages hybrides sont antérieurs à Python par un étirement loooooong. Le lisp, par exemple, date de 1959 et est, en fait, un langage multi-paradigme. Il prend pleinement en charge les approches fonctionnelles, les approches procédurales et les approches orientées objet de la programmation. Avec les bons packages de macro sur le dessus, vous pouvez également faire de la programmation logique assez facilement. Le schéma est également antérieur à Python (et à cet article). Cela remonte à 1975. Peut-être devriez-vous jeter un coup d'œil à cette chronologie des langages de programmation .
JUSTE MON AVIS correct le