Cela a été dit avant tout est fonction (avec énormément de récessivité et d'imbrications d'appels), pas de variables mais utilisation de listes/ tuples,pas de types(**) ... et des fonctions essentielles comme map, filter, reduce, ...
Des concepts très importants sont nécessaires : lambda/ fonctions anonymes, récursion terminale ("tail-recursive"), les fonctions d'ordre supérieur, [la transparence référentielle, les effets de bord, les fonctions pures, la composition de fonctions, la curryfication, l’arité (*)], ...
Je me demande si le fait que ce soit des langages interprétés avec 1 environnement d'exécution est 1 condition valable
Après vérification, Lisp a des variables, mais il me semble que c'est à l'exécution : passage de paramètres, récupération de résultat, ...
* : trouvé sur 1 autre site, là je sèche - à prendre avec des pincettes.
** : effectivement certains langages fonctionnels ont 1 typage statique
Hmmm, après lecture de Wikipedia, je viens de comprendre la différence entre "programmation procédurale" et "programmation fonctionnelle"... et "POO"
Seulement, j'avoue ne toujours pas comprendre !
En effet...
La POO englobe la programmation procédurale.
Et la programmation fonctionnelle n'est qu'un paradigme parmi d'autres qui implique qu'une fonction ne peut pas modifier de données autres que locales.
Je ne vois absolument pas en quoi c'est incompatible avec la POO !
Tout du moins, si, je vois, mais rien n'empêche de travailler proprement avec les deux en même temps.
J'ai appris en cours il y a longtemps qu'une "procedure fait quelque chose" (et donc modifie potentiellement les données passées en paramètre ou globales) tandis qu'une "fonction vaut quelque chose", et dans ce cas, autant elle retourne une valeur, autant elle ne modifie rien aux données autre que locales (faire de la programmation fonctionnelle revient donc à n'utiliser que des "fonction" répondant à cette définition).
Ceci est vrai avec le langage ADA, qui a servit d'illustration à mes cours, mais aussi en T-SQL (langage procédurale de SQL Server).
Il me semble que c'est vrai aussi avec le Pascal.
VB par contre, non, rien n'empêche une fonction de modifier les données passées en paramètre, ou de modifier un objet. En revanche la procédure ne peut jamais rien retourner comme résultat.
Après, la plupart des langages POO sont dérivées de près ou de loin du C, qui ne fait pas la différence entre procédure et fonction : il n'y a que des fonctions, et vogue la galère avec des fonctions qui valent et font à la fois des choses dans la plus grande impunité.
Cependant, rien n'empêche d'imposer comme règle de développement d'effectuer la distinction entre "fonction" (fonction ou méthode qui retourne une valeur) et "procédure" (fonction ou méthode qui retourne void).
Et à ce moment d'interdire à une "fonction" d'appeler une procédure ni de modifier quoi que ce soit d'autre que des variables locales.
Je pense même qu'avec un peu de travail, il doit être possible de mettre en place des règles de validation du code qui s'en assurent, aussi bien dans Visual Studio que dans Eclipse.
A partir de là, la programmation fonctionnelle est parfaitement possible en POO.
Cependant, je ne vois pas comment on peut travailler sans jamais rien modifier... y'a bien un moment où le résultat des opérations doit être stocké quelque part, en lieu de place des données d'origine qui sont devenues caduques... un programme ne peut être composé que de fonctions, ou alors il ne fait rien (par définition, mais il vaut quelque chose, c'est déjà ça ).
Y'a bien un moment où on devra utiliser une procédure, que ce soit pour mettre à jour les données dans une base, un fichier, ou à l'écran !
D'après la page wiki en anglais, Purely functional programming, le Lisp n'est pas un langage fonctionnel pur
La "pureté" repose sur la non-mutabilité des données ("Purely functional programming may also be defined by forbidding changing-state and mutable data.") mais également sur des fonctions pures qui retournent la même chose pour 1 même argument (avec 1 idée de persistance et de désactiver les effets de bords)
Après vérifications, cela rejoint mon précédent message et tout ce que tu décris est lié aux fonctions pures.
Mais pas forcément. En C++, langage objet, il faut utiliser le C++ moderne (>= C++11) pour avoir les lambdas et les fonctions d'ordre supérieure. Sinon, tu ne peux pas faire de programmation fonctionnelle.
Je ne connais pas ces langages , mais d'après ce que j'ai pu comprendre, le typage n'a rien à voir avec le paradigme fonctionnel
Le typage statique permet 1 compilation et donc 1 détection de bogues (pas tous) - et donc c'est plus rassurant
Mais je me demande si ces langages sont encore interprétés, si ce sont que des "scripts"
N'est pas la définition d'une fonction déterministe ?
Hmmm, oui, plus, ça doit être une "vraie" fonction dans la mesure où elle ne doit rien faire.
Les lambdas ne sont-elles pas au final une simple écriture plus élégante des delegate ou même pointeur de fonction ? Si je ne m'abuse, cette dernière est bien plus ancienne que C++ 11 non ?
Je ne suis pas expert sur les termes , mais d'après ce que j'ai compris oui c'est 1 fonction déterministe (qui n'utilise pas 1 variable globale, statique ou saisie par l'utilisateur), et 1 fonction qui ne modifie pas ses paramètres.
Avec les lambdas, tu as la notion de fermeture ("closure" en anglais) absent des pointeurs de fonction.
Et n'oublie pas qu'en programmation fonctionnelle tout est fonction - donc cela va plus loin que coder des delegates mais passer 1 fonction en paramètre (par exemple exemple, multiplier par 2 tous les éléments d'1 liste), retourner 1 fonction (et ainsi la stocker dans 1 variable pour l'appeler avec différents paramètres).
Ce genre de code
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 function makeAdder(x) { return function(y) { return x + y; }; } var add5 = makeAdder(5); var add10 = makeAdder(10); console.log( add5(2) ); // 7 console.log( add10(2) ); // 12
Édit : En C++ tu as les foncteurs - les classes dont on surcharge l'opérateur (), et qui permet de les appeler comme une fonction. 1 foncteur permet de faire des fonctions avec 1 contexte/ des états.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 class makeAdder { public: makeAdder(int input_x = 0) : x(input_x) {} int operator() (int y) { return (y + x); } private: int x; }; // .. makeAdder add5(5); makeAdder add10(10); std::cout << "5 + 2 = " << add5(2) << std::endl << "10 + 2 = " << add10(2) << std::endl;
Exact, bien vu. Ca existe depuis les années 90 en fait, mais les lambda C++ 11 simplifient quand même beaucoup:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 std::function<int(int)> makeAdder2(int x) { return [x](int y) { return y + x; }; } // ... auto add5 = makeAdder2(5); auto add10 = makeAdder2(10); std::cout << "5 + 2 = " << add5(2) << std::endl << "10 + 2 = " << add10(2) << std::endl;
Le code de ta fonction "function(y)" me laisse perplexe.
En effet, tu lui passes en paramètre y et elle retourne x + y.
Par conséquent, elle utilise le paramètre x de makeAdder qu'elle n'a pas reçu : n'est-ce pas justement l'inverse d'une fonction pure ? Je ne vois aucune différence entre x ici et un membre readonly dans une classe.
Aussi, et là je pige plus rien :
add5 et add10 sont manifestement des variables.
Quant à console.log() il s'agit de toute évidence d'une procédure et non d'une fonction : elle ne retourne pas de paramètre, et modifie l'état de la sortie écran.
Enfin, je ne vois pas ce qui change (je parle fonctionnellement parlant, ou algorythmement parlant, pas syntaxiquement ou matériellement parlant) par rapport à un code objet classique :
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 using System; namespace Fonctionnel { class Program { static void Main() { Adder add5 = new Adder(5); Adder add10 = new Adder(10); Console.WriteLine($"5 + 2 = {add5.Add(2)}"); Console.WriteLine($"10 + 2 = {add10.Add(2)}"); } } class Adder { readonly int X; public Adder(int x) { X = x; } public int Add(int y) { return this.X + y; } } }
Bon, j'imagine qu'avec un exemple plus complexe, c'est moins trivial, mais pour le coup, je ne vois pas du tout la moindre différence en termes de "sécurité" du code notamment.
Si le débat ne concerne que la syntaxe, alors le débat est clos depuis la fin des années 70 quand les langages impératifs sont devenus objet et ont enterré les langages fonctionnels... justement à cause de la syntaxe indigeste et impossible à maintenir.
C'est justement ça la notion de fermeture. La fonction "interne" est définie par rapport à la fonction englobante. On a donc une fonction qui construit et retourne une fonction (qui n'est donc pas toujours la même). Et ça on ne peut pas le faire de façon pure avec les pointeurs de fonction.
Oui, mais ce sont aussi des fonctions. En fonctionnel, on dit que les fonctions sont des citoyens de première classe car elle sont une valeur comme une autre et peuvent donc être affectées à des variables.
Oui, c'est une procédure. JavaScript n'est pas "pur" fonctionnel et fait des side-effects pour l'affichage.
Déjà ton code est plus long et surtout tu perds la notion de "fonction appelable". Avec le foncteur, tu sais que tu peux l'appeler juste en mettant les paramètres entre parenthèse. Dans ton code, tu dois savoir quelle méthode appeler. Ca n'a l'air de rien mais c'est très important car un foncteur peut être passé en paramètre, de façon homogène, à une autre fonction, par exemple pour des fonctions map, filter ou reduce.
J'avoue que ça ne me parle pas.
En fait, je ne vois que des différences syntaxiques.
Ca pourrait tout aussi bien être un débat "le BASIC c'est mieux que le C car y'a pas besoin de mettre des points virgule ni des accolades, c'est moins long à taper".
Je ne vois pas ce qu'il est possible de faire/garantir en "fonctionnel" qu'on ne saurait faire avec les mêmes garanties en "POO".
Je persiste et signe, j'ai surtout l'impression que le souci réside dans l'absence de différence entre "procédure" et "fonction", ainsi qu'entre "fonction déterministe ou non".
En effet, en POO on peut parfaitement avoir une méthode Add() qui retourne le résultat d'une somme, mais aussi qui modifie l'objet. Parfois les deux, ce qui est d'autant plus déroutant.
Mais à mon sens, le débat n'est pas de savoir s'il faut faire de la POO ou du fonctionnel, mais avant tout de se mettre d'accord sur des règles de codage et s'entendre sur la sémantique.
Pour le reste, en effet, si demain ma touche ";" ne fonctionne plus sur mon clavier, en attendant d'en racheter un autre, j'utiliserai VB... Mais c'est pas une justification "valable" pour dire que VB est préférable à C#.
Enfin, dire que :
- moins de code c'est mieux
- les expression lambda qui permettent de faire un ERP en une ligne c'est mieux
- encapsuler sur 25 niveaux avec chacun 2 lignes c'est mieux que d'avoir 25 fonctions de même niveau de 2 lignes
A mon sens c'est avant tout une question de goût (débat de syntaxe toujours).
Et de mon point de vue perso, moins de code, c'est pas forcément mal, mais les lambas, les types anonymes ou implicites (var), et les encapsulations à gogo avec rappel du contexte parent, ce sont avant tout des usines à bug, que chaque personne qui devra débugger va devoir réécrire totalement faute de comprendre ce que l'auteur original à voulu faire.
Autant un truc comme :
Je trouve ça lisible et reste raisonnable, autant ça peut vite devenir un bordel impossible à comprendre (par exemple avec Linq en C# où tu peux arriver à des spaghettis indigestes)
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 decimal somme = MaList.Sum(a => a.Quantity * a.Price);
=> Là, sans relire toute la définition de la classe db, impossible de déterminer ce que ça va réellement me ramener... quant au type retourné, toujours pas pigé l'intérêt puisqu'après on ne peut rien en faire (pas de méthode, impossible de le passer en paramètre à une méthode qui ne saura pas quoi en faire, etc.)
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 var query = from product in db.Products where product.ProductId == productId select new { Product = product, Approvers = product.Approvers.Where(pa => pa.Version == version) };
C'est exactement le sujet de l'article. Tu dis ça parce que tu as l'habitude du procédural/POO mais si tu avais l'habitude du fonctionnel, tu dirais exactement le contraire. Aujourd'hui on se lance dans de l'objet à la java/c++ par réflexe et l'article pose la question de savoir si c'est vraiment une bonne idée. Et quand on remarque que même les langages mainstream (java, c++, etc) intègrent du fonctionnel depuis quelques années (lambdas, etc), on peut penser que non ce n'est pas une bonne idée.
Justement l'idée du typage c'est de savoir ce qu'on manipule et de le vérifier automatiquement par le compilateur. Mais c'est un autre débat, un peu indépendant du fonctionnel.
J'ai quand même l'impression qu'on est sur un débat universitaire sans lien avec la réalité du terrain.
Personnellement, aussi bien en tant que développeur, que chef de projet, je vais choisir C# ou Java (et non C++ ou VB) pour deux raisons, et deux raisons seulement :
- Ces langages, même si ce ne sont pas les plus adaptés à une problématique donnée, sauront apporter une réponse viable à ma problématique
- Moi-même ou mes équipes maîtrisent suffisamment ces langages pour produire un code rapidement, fiable et maintenable
Donc à aucun moment je ne choisirai F# ou LISP, non pas parce qu'ils sont fonctionnels, mais parce que :
- Je ne suis pas certain qu'ils soient adaptés à la résolution de toutes les problématiques (probablement parce que je ne les connais pas)
- Personne dans mon entourage n'est capable de pondre la moindre ligne de ces langages sans suivre 2 jours de tutoriels, et après ces deux jours, personne dans mon équipe n'aura le recul nécessaire pour justifier pourquoi il a écrit un code plutôt qu'un autre
C'est pour ces mêmes raisons que je ne choisirai pas C++ ni VB (même si pour ce dernier j'ai l'expérience nécessaire, mais j'ai pas envie )
En revanche, si demain j'utilise depuis mon programme C# ou Java une librairie externe, je serai indifférent à la techno "interne" de cette dernière du moment qu'elle est stable et fiable. Ca peut être du LISP, du C# ou même du JavaScript, du moment qu'elle fait ce que je lui demande, pas de souci.
En revanche, jamais de la vie je ne vais dépenser plus de 2 minutes à me former moi-même ou mes équipes sur un langages particulier "parce qu'il peut être un peu mieux ou plus adapté pour certaines choses, mais pas tout le temps, et au final n'apporte franchement... rien ou presque".
Finalement, pour en revenir à la syntaxe "fonctionnelle" qui intègre petit à petit la "POO", j'ai un avis très mitigé.
Les expression Lambda par exemple peuvent être très intéressantes pour des opérations simples, mais peuvent aussi s'avérer être des usines à gaz.
Par plus tard que la semaine dernière, je suis tombé sur ça dans un programme qui imprime une facture :
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 decimal prixTotal = Produits.Sum(x => x.Quantite * x.PrixUnitaire); int quantiteTotale = Produits.Sum(x => x.Quantite); int nombreArticles = Produits.Sum(x => 1);
Certes, ça aurait été plus long à taper, mais je m'attendais plutôt à ce code, qui est 3 fois plus rapide, à quelques pouillèmes près :
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 decimal prixTotal = 0; int quantiteTotale = 0; int nombreArticles = Produits.Length; foreach (Produit p in Produits) { prixTotal += p.Quantite * p.PrixUnitaire; quantiteTotale += p.Quantite; }
Donc les expressions Lambda c'est pas mal, mais ça dispense un peu de réfléchir, et ça peut vite nuire non pas à la lisibilité du code (quoique...) mais surtout à ces performances.
Le souci, c'est que si au début le programme n'a besoin que du prix total, on va choisir en toute logique l'expression lamba, car plus rapide à écrire et plus lisible.
Mais si ensuite on commande à demander de nouvelles évolutions, à quel moment doit-on faire le choix de réécrire toute une partie du programme, pour préserver les performances ?
Bref, d'ici quelques années (enfin, déjà maintenant, nos téléphones ont plus de puissance de calcul que l'infra de tous les projets Apollo réunis, et rament rien que pour afficher un SMS) on va se retrouver avec des programmes qui se seront transformés en usine à gaz... pas sûr que ce soit un vrai gain au final.
Je pense que c'est encore cette question d'habitude. Je n'ai pas connu cette époque mais j'imagine que quand la POO est apparue dans les années 70/80, beaucoup de devs fortran se sont dit qu'ils n'en voyaient pas l'intérêt et que ça n'apportait rien au final.
Dans un "vrai langage fonctionnel", on écrirait plutôt quelque chose comme ça. Et avec un peu d'habitude, on voit le "reduce" et on a tout de suite une bonne idée de ce que ça fait.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 calcul = p (prix, quantite) => (prix + p.PrixUnitaire*p.Quantite, quantite + p.Quantite) (prixTotal, quantiteTotal) = reduce(Produits, (0, 0), calcul)
Non parce que lambda + closures + currying + partial application = un formidable moins natif d'effectuer de l'injection de dépendance.
Tu passes des fonctions qui font des I/O en paramètre et tu te retrouves avec un mécanisme d'injection simple comme bonjour qui te facilite tellement l'écriture de tes tests que ça devient simple comme bonjour.
Ca me fait bizarre de dire ça, mais 41 ans, ça doit être un cap, je suis entrain de me transformer en dinosaure... Je pense qu'à ce époque, les développeurs Fortran n'ont pas vu l'intérêt de passer à la POO, car effectivement, ça n'apportait aucune solution à des problématiques qu'ils ne savaient déjà résoudre.
A mon sens, le débat est le même ici (quoi que c'est pire, car on tente une espèce de fuite en arrière) : les langages fonctionnels apportent certaines solution élégantes pour résoudre certaines problématiques que la POO sait déjà résoudre, et à côté de ça imposent des restrictions pour d'autres traitements que la POO résout de façon plus élégante.
A partir de là, j'ai envie de dire que chacun choisi l'outil qui lui convient en fonction de son appétence pour l'une ou l'autre des solutions.
Je n'irais pas dire que l'un est mieux que l'autre, simplement que je ne vois pas l'intérêt d'imposer l'un contre l'autre.
Le Fortran est mort principalement parce qu'il a arrêté d'évoluer, et que "politiquement" il a été décidé qu'il fallait le mettre au placard.
Microsoft a inventé le Basic à peu près à la même époque, et pourtant VB.NET est toujours très utilisé. Alors oui, je suis d'accord, ça n'a rien à voir ou presque avec le BASIC de l'époque... Mais rien n'aurait empêché d'avoir un Fortran.NET ou autre, qui aurait implémenté des évolutions supportant la comparaison avec C++ ou Java, ce qu'a fait Microsoft en portant VB sur son Framework .NET.
Et quand on y regarde bien, C++ 11 n'a plus rien à voir sur bien des points avec la première mouture, et un programme en C des années 80 ne compilera pas sur un gcc flambant neuf (alors que le *.exe généré à l'époque peut parfaitement encore tourner sous certaines conditions).
Je ne parlais pas de ce débat, mais celui de décider d'abandonner un langage (et toute une méthodologie) au profit d'un autre sous prétexte que pour faire ça ou ça, parfois c'est plus adapté.
Si tu as des équipes de 200 personnes, tu peux te permettre d'en former 10, 15, 20% sur une dizaine de langages/technos différentes, puis "faire tes courses" en fonction d'un besoin précis (à condition que toi-même ait suffisamment de recul sur chaque langage/techno pour faire un choix qui tienne la route).
Mais la réalité est généralement autre, les équipes sont souvent bien plus réduites, et le chef de projet est mono ou bi-techno maxi. Donc habitude ou non, il n'a pas vraiment d'autre choix que d'utiliser les outils qu'il a à disposition et que son équipe sait utiliser.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager