Langage (s) de programmation fonctionnel (s) le plus pur? [fermé]


20

Je suis intéressé par un meilleur apprentissage de la programmation fonctionnelle. Pour ce faire, il me semble évident que je devrais me forcer à utiliser le langage de programmation fonctionnel le plus pur possible. Par conséquent, je demande ici, plus ou moins, un ordre des langages de programmation fonctionnels en fonction de leur pureté.

Il me semble qu'il serait plus pratique d'apprendre Lisp ou Clojure (ou Scheme, ou Scala, etc.), mais pour ce que j'ai entendu récemment, Haskell serait très difficile à battre pour enseigner les principes de programmation fonctionnelle à quelqu'un. Je n'en suis pas encore sûr, alors je vous demande: quel est le langage de programmation fonctionnel le plus pur? Une commande serait formidable si plusieurs se disputent le magnifique titre du langage de programmation fonctionnel le plus pur.


2
J'ai appris Miranda à l'université, donc je suis partisan, mais je suggérerais Haskel à tous ceux qui veulent s'immerger dans un langage fonctionnel sans les distractions de l' impureté . * 8 ')
Mark Booth

2
Après avoir terminé l'apprentissage de la programmation fonctionnelle, vous devez également apprendre la programmation typée statiquement avec un système de type expressif. Dans la catégorie combinée (à la fois fonctionnelle et typée), je suggère: Coq> Haskell> OCaml> Scala> autres. Il existe des alternatives moins populaires qui se situent entre Coq et Haskell (comme Epigram et Agda). Haskell manque le système de module expressif d'OCaml.
lukstafi

Réponses:


28

Il n'y a pas d'échelle pour évaluer le degré de pureté des langages fonctionnels. Si le langage permet des effets secondaires, c'est impur, sinon c'est pur. Selon cette définition, Haskell, Mercury, Clean, etc. sont de purs langages fonctionnels; tandis que Scala, Clojure, F #, OCaml etc. sont impurs.

EDIT: Peut-être que j'aurais dû formuler ceci comme "si le langage ne permet pas d'effets secondaires sans en informer le système de type , c'est pur. Sinon c'est impur.".


6
Pas tout à fait: Haskell autorise les effets secondaires ( IOmonade). C'est juste que le code provoquant des effets secondaires est clairement marqué comme tel. Je ne pense pas du tout qu'il soit utile de parler de langages purs / impurs (Haskell vous permet de programmer impérativement! Gasp!) /impur.
Frank Shearar

8
@Frank: Oui, Haskell autorise les effets secondaires, mais il fait plus que simplement marquer du code provoquant des effets secondaires. Il maintient également la transparence référentielle même lorsque vous utilisez du code provoquant des effets secondaires et c'est ce qu'il rend pur - du moins selon la définition de la pureté de nombreuses personnes. Bien sûr, cela ne correspond pas à la définition de missingfaktor de "aucun effet secondaire autorisé".
sepp2k

4
@Frank Shearar mais il est utile de parler de cette manière car la IOmonade est pure. C'est la bibliothèque d'exécution qui ne l'est pas, pas votre code, car votre mainfonction est essentiellement un énorme transformateur d'état. (Quelque chose comme main :: World -> Worlddans les coulisses)
alternative

1
Ma langue a également une transparence référentielle. Vous venez d'écrire program = "some C code", puis l'environnement d'exécution traite du code C. :-)
Daniel Lubarov

3
Comme l'impression à l'écran est un effet secondaire, les programmes fonctionnels vraiment purs sont assez ennuyeux.

15

Puisque l'apprentissage est votre objectif, et non l'écriture de programmes en soi, vous ne pouvez pas obtenir plus pur que Lambda Calculus .

Lambda Calculus existait avant l'invention des ordinateurs. Il a fallu plusieurs logiciens qualifiés pour travailler sur la façon de faire la soustraction (pendant un certain temps, il a été théorisé que seules l'addition et la multiplication étaient possibles).

Apprendre comment les booléens et les nombres ifpeuvent être inventés à partir de rien ne mettra plus de gaz dans votre réservoir, mais cela rendra votre réservoir beaucoup plus grand.


Certes, il n'y a probablement rien de plus pur que cela, mais nous franchissons un peu trop la barrière des mathématiques. Je veux toujours pratiquer la programmation et apprendre un nouveau langage tout en absorbant les principes fonctionnels. Cependant, je conviens qu'il pourrait être intéressant d'étudier le calcul lambda juste pour mieux comprendre les fondements du paradigme fonctionnel (et avoir un réservoir plus grand).
Joanis

2
Écrire une épreuve, c'est écrire un programme et écrire un programme, c'est écrire une épreuve. Lorsque vous en apprendrez plus sur l'isomorphisme de Curry-Howard, vous vous rendrez compte que vous avez passé cette barrière mathématique il y a dix mille lignes.
Macneil

1
Il n'y a pas de représentation simple de -1.
Daniel Lubarov

2
@Mason Wheeler: le λ-calcul seul n'a pas de nombres, ni vraiment aucune sorte de données en plus des abstractions lambda. Sauf indication contraire, quiconque parle de nombres dans le λ-calcul signifie probablement le codage de Church pour les nombres naturels , où un nombre n est représenté par une composition de fonctions n fois. Cela dit, je suis douteux était que la lutte beaucoup plus d'une soustraction à comprendre une fois que quelqu'un a essayé en fait; Je l'ai élaboré par moi-même en un après-midi, étant donné uniquement la définition de l'addition et la connaissance que la soustraction était possible.
CA McCann

1
La soustraction à l'aide des chiffres de l'Église est facile une fois que vous avez décomposé par cas. Construire l'ensemble du calcul (dérivé) pour prendre en charge les nombres négatifs, plutôt plus de travail.
Donal Fellows

6

Les langages impurs ne diffèrent pas vraiment en principe des langages impératifs plus familiers, surtout maintenant que de nombreuses astuces fonctionnelles ont été copiées. Ce qui est différent, c'est le style - comment résoudre les problèmes.

Que vous comptiez Haskell comme pur, ou que vous comptiez la monade IO comme impureté, le style Haskell est une forme extrême de ce style et mérite d'être étudié.

La monade Haskell IO est dérivée de la théorie mathématique des monades (bien sûr). Cependant, pour les programmeurs impératifs, je pense qu'une manière à l'envers d'arriver aux monades a plus de sens.

Phase un - un langage fonctionnel pur peut facilement renvoyer une grande valeur de chaîne comme résultat. Cette grosse chaîne peut être le code source d'un programme impératif, dérivé de manière purement fonctionnelle de certains paramètres spécifiant les exigences. Vous pouvez ensuite créer un compilateur de "niveau supérieur" qui exécute votre générateur de code, puis alimente automatiquement ce code généré dans le compilateur de langage impératif.

Phase deux - plutôt que de générer du code source textuel, vous générez un arbre de syntaxe abstraite fortement typé. Votre compilateur en langage impératif est absorbé dans votre compilateur de "niveau supérieur" et accepte l'AST directement comme code source. C'est beaucoup plus proche de ce que fait Haskell.

C'est toujours gênant, cependant. Par exemple, vous avez deux types de fonctions distinctes - celles évaluées pendant la phase de génération de code et celles exécutées lors de l'exécution du programme généré. C'est un peu comme la distinction entre les fonctions et les modèles en C ++.

Donc, pour la phase 3, rendez les deux identiques - la même fonction avec la même syntaxe peut être partiellement évaluée pendant la «génération de code», ou entièrement évaluée, ou pas du tout évaluée. En outre, jetez tous les nœuds AST de construction en boucle en faveur de la récursivité. En fait, rejetez l'idée des nœuds AST en tant que type spécial de données - ne disposez pas de nœuds AST à "valeur littérale", mais simplement de valeurs, etc.

C'est à peu près ce que fait la monade IO - l'opérateur de liaison est un moyen de composer des "actions" pour former des programmes. Ce n'est rien de spécial - juste une fonction. De nombreuses expressions et fonctions peuvent être évaluées lors de la "génération de code", mais celles qui dépendent des effets secondaires d'E / S doivent avoir une évaluation retardée jusqu'à l'exécution - non pas par une règle spéciale, mais comme une conséquence naturelle des dépendances des données dans expressions.

Les monades en général ne sont que de la généralisation - elles ont la même interface, mais implémentent les opérations abstraites différemment, donc au lieu d'évaluer une description de code impératif, elles évaluent plutôt autre chose. Avoir la même interface signifie qu'il y a certaines choses que vous pouvez faire aux monades sans vous soucier de quelle monade, ce qui s'avère utile.

Cette description fera sans aucun doute exploser les têtes des puristes, mais elle explique pour moi certaines des vraies raisons pour lesquelles Haskell est intéressant. Il brouille la frontière entre programmation et métaprogrammation, et utilise les outils de programmation fonctionnelle pour réinventer la programmation impérative sans avoir besoin d'une syntaxe particulière.

Une critique que j'ai des modèles C ++ est qu'ils sont une sorte de sous-langage fonctionnel pur cassé dans un langage impératif - pour évaluer la même fonction de base au moment de la compilation plutôt qu'au moment de l'exécution, vous devez la réimplémenter en utilisant un style complètement différent de codage. Dans Haskell, bien que l'impureté doive être étiquetée comme telle dans son type, la même fonction exacte peut être évaluée à la fois dans un sens de méta-programmation et dans un sens d'exécution hors méta-programmation dans le même programme - il n'y a pas de ligne dure entre programmation et métaprogrammation.

Cela dit, il existe des métaprogrammations que Haskell standard ne peut pas faire, essentiellement parce que les types (et peut-être quelques autres choses) ne sont pas des valeurs de première classe. Il existe cependant des variantes linguistiques qui tentent de résoudre ce problème.

Beaucoup de choses que j'ai dites sur Haskell peuvent être appliquées dans des langages fonctionnels impurs - et parfois même des langages impératifs. Haskell est différent parce que vous n'avez pas d'autre choix que d'adopter cette approche - cela vous oblige essentiellement à apprendre ce style de travail. Vous pouvez «écrire C en ML», mais vous ne pouvez pas «écrire C en Haskell» - du moins pas sans savoir ce qui se passe sous le capot.


Je vous remercie! Je me demandais si la généricité des modèles C ++ aurait pu être unifiée en fonctions elles-mêmes. On dirait que Haskell fait cela, mais l'important est de savoir si cela peut être implémenté dans un langage compilé , de sorte que le même moteur créant des fonctions génériques à partir de modèles pendant la compilation, peut également les évaluer pendant l'exécution ...
Milind R

5

Personnellement, je classe les langues en trois niveaux de pureté fonctionnelle:

  • Langages fonctionnels purs - c'est-à-dire ceux qui traitent l'intégralité de votre programme comme une fonction pure et gèrent la mutabilité uniquement par l'interaction avec le runtime - Haskell est probablement l'exemple canonique

  • Langages fonctionnels impurs - c'est-à-dire ceux qui mettent l'accent sur un style fonctionnel mais qui permettent des effets secondaires. Clojure est clairement dans cette catégorie (il permet la mutation de façon contrôlée dans le cadre de son framework STM), également OCaml ou F #

  • Langages multi-paradigmes - ce ne sont pas des langages fonctionnels avant tout, mais peuvent prendre en charge un style fonctionnel en utilisant des fonctions de première classe, etc. Scala est un bon exemple ici, je mettrais également Common Lisp dans cette catégorie et vous pourriez même inclure des langages comme JavaScript .

Dans votre situation, je suggère d'abord d'apprendre Haskell, puis Clojure. C'est ce que j'ai fait et cela a très bien fonctionné pour moi! Haskell est magnifique et vous enseigne les principes fonctionnels les plus purs, Clojure est beaucoup plus pragmatique et vous aide à faire beaucoup de choses tout en étant très fonctionnel dans l'âme.

Je ne considère pas vraiment la troisième catégorie comme des langages fonctionnels (bien qu'après avoir appris Haskell et Clojure, je me retrouve souvent à profiter des techniques fonctionnelles lors de leur utilisation!)


Dans des limites très strictes , même C a des capacités fonctionnelles. (La principale limitation concerne la synthèse d'exécution des fonctions, ce qui est vraiment horrible en C, à tel point que la plupart des gens prétendent que cela ne peut pas être fait.)
Donal Fellows

2
@Donal Fellows: Dans la limite où la bêtise va à l'infini, chaque langage Turing-complet est fonctionnel: il suffit d'implémenter un interpréteur Lisp dans le langage, puis de l'utiliser. :]
CA McCann

Les paradigmes sont dénués de sens si vous considérez que tout est Turing complet et peut donc imiter tout le reste. Lorsque vous pensez aux paradigmes, vous devez vous concentrer sur ce que le langage rend idiomatique et ce qu'il décourage / empêche. Pour compter comme un langage fonctionnel à mon avis, la mutation et les effets secondaires doivent être unidiomatiques (pour la PF impure) ou interdits (pour la PF pure). Cela signifie que C n'est pas fonctionnel. Les langages multi-paradigmes ne sont pas non plus comme Scala.
mikera

@mikera: Je trouve votre réponse très intéressante: si Scala n'est pas fonctionnel mais seulement multi-paradigme, comment jugez-vous les fonctionnalités de programmation fonctionnelle en C ++ 11, C # ou même Java (l'introduction prévue de lambdas)?
Giorgio

@Giorgio: Je pense que les fonctionnalités dans tous les langages que vous mentionnez sont probablement une bonne idée, elles n'en font tout simplement pas des "langages fonctionnels". Ou pour le regarder dans la direction opposée, le fait que vous puissiez faire des E / S monadiques de style impératif ne fait pas de Haskell un langage impératif :-). Les langages multi-paradigmes sont essentiellement le résultat lorsque vous rassemblez des fonctionnalités de nombreux paradigmes différents et ne mettez pas de style particulier en premier lieu. IMHO C ++, C # et Java évoluent tous vers le multi-paradigme, mais ne sont pas encore là car la POO basée sur les classes est toujours dominante.
mikera

3

Si un langage fonctionnel pur est tel, qui n'a que des fonctions pures (routines qui n'ont pas d'effets secondaires), alors c'est un peu inutile, car il ne peut pas lire d'entrée ou écrire de sortie;)

Parce que c'est vraiment pour apprendre, je pense que l'isolement n'est pas nécessairement la voie à suivre. La programmation fonctionnelle est un paradigme. Il est important de comprendre quels paradigmes conviennent à quels problèmes et, surtout, comment les combiner au mieux.

Je vais le dire maintenant: les modes de programmation sont stupides et contre-productifs. La seule chose qui compte, c'est que votre programme soit court, facile à écrire, facile à entretenir et fonctionne correctement. Comment y parvenir n'a rien à voir avec la programmation des modes. - Richard Jones

En dehors de cela, si vous recherchez la "pureté", vous voudrez peut-être jeter un œil à Pure . Notez cependant que l'extrême facilité d'appeler des routines C le rend fonctionnellement impur (mais aussi très puissant).


3

Pas une réponse entièrement sérieuse, mais Unlambda doit être un concurrent. Vous ne pouvez pas obtenir plus de fonctionnalités «pures» que les combinateurs SKI.


4
La démence! Hérésie! Quelle sorte de langage pur a des absurdités comme des combinateurs avec des effets secondaires? Non non. Qu'est - ce que vous voulez vraiment ici est Lazy K .
CA McCann

2

Erlang, Haskell, Scheme, Scala, Clojure et F #

Cette question vous aiderait probablement mieux dans votre recherche également .


Merci, question intéressante en effet. J'ai commencé avec PLT-Scheme / Racket, mais je n'ai pas encore regardé SICP ... Real World Haskell me semble assez intéressant aussi.
Joanis

Le schéma encourage un style fonctionnel, mais il a set!(entre autres) ...
Daniel Lubarov

Je suggère OCaml, car c'est mon préféré. Et il a un système de modules, que Haskell et F # manquent.
lukstafi

@lukstafi haskell a peut-être changé au cours des 3 dernières années (je sais que c'est devenu fou), mais il y a certainement des modules dans haskell.
sara

@kai Par système de modules, j'entendais des modules paramétrés par d'autres modules (un "lambda calcul" de modules).
lukstafi
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.