Existe-t-il une méthodologie d'ingénierie logicielle pour la programmation fonctionnelle? [fermé]

203

Le génie logiciel tel qu'il est enseigné aujourd'hui est entièrement axé sur la programmation orientée objet et la vision orientée objet «naturelle» du monde. Il existe une méthodologie détaillée qui décrit comment transformer un modèle de domaine en un modèle de classe avec plusieurs étapes et de nombreux artefacts (UML) comme des diagrammes de cas d'utilisation ou des diagrammes de classe. De nombreux programmeurs ont internalisé cette approche et ont une bonne idée de la façon de concevoir une application orientée objet à partir de zéro.

Le nouveau battage médiatique est la programmation fonctionnelle, qui est enseignée dans de nombreux livres et tutoriels. Mais qu'en est-il de l'ingénierie logicielle fonctionnelle? En lisant sur Lisp et Clojure, je suis arrivé à deux déclarations intéressantes:

  1. Les programmes fonctionnels sont souvent développés de bas en haut plutôt que de haut en bas («On Lisp», Paul Graham)

  2. Les programmeurs fonctionnels utilisent des cartes où les programmeurs OO utilisent des objets / classes («Clojure for Java Programmers», exposé de Rich Hickley).

Quelle est donc la méthodologie pour une conception systématique (basée sur un modèle?) D'une application fonctionnelle, c'est-à-dire en Lisp ou en Clojure? Quelles sont les étapes courantes, quels artefacts dois-je utiliser, comment les mapper de l'espace du problème à l'espace de la solution?

Thorsten
la source
3
J'ai un commentaire ici: de nombreux programmes sont écrits de manière descendante, une exposition pratique du processus de développement de logiciels dans un langage fonctionnel est donnée dans le livre "Functional Programming in Concurrent Clean" (le langage lui-même est très académique, bien que).
Artyom Shalkhakov
4
1. Parnas fait valoir que la plupart des programmes devraient être ascendants puis truqués pour ressembler à de haut en bas, donc ces approches devraient être mélangées, il n'y a pas de bonne réponse.
Gabriel Ščerbák
2
2. Les objets fournissent un comportement en fonction de leur état structuré encapsulé, dans FP vous avez tous l'état et la structure explicites et le comportement (fonctions) est séparé de la structure. Donc, pour la modélisation des données, vous utilisez des cartes pour les objets, mais lors de la conception d'applications, les objets ne peuvent pas être remplacés par des fonctions - FP est une grande expression générée et évaluée via des pipelines, la POO consiste à créer le modèle et à envoyer des messages entre les objets.
Gabriel Ščerbák
1
J'ai posé une question connexe quelque temps en arrière: "comment modélise- t-on les données des bases de données relationnelles dans clojure?" stackoverflow.com/questions/3067261/…
Sandeep
4
Hehe, dans l'une des conférences du SICP, Hal Abelson dit, à moitié en plaisantant, quelque chose du genre "Il existe une méthodologie célèbre, ou devrais-je dire la mythologie, appelée génie logiciel [...] faisant des diagrammes et des exigences compliqués, puis construisant systèmes avec eux, ces gens n'ont pas beaucoup programmé ". Je viens d'une "école Java", où pendant des siècles, nous avons enseigné l'UML, les artefacts et d'autres choses, et même si un peu est bon, trop de planification et de schémas (jeu de mots) est plus nuisible qu'utile: vous ne savez jamais comment votre le logiciel sera jusqu'à ce que vous arriviez à réellement coder.
lfborjas

Réponses:

165

Dieu merci, les ingénieurs en logiciel n'ont pas encore découvert la programmation fonctionnelle. Voici quelques parallèles:

  • De nombreux "modèles de conception" OO sont capturés en tant que fonctions d'ordre supérieur. Par exemple, le modèle Visitor est connu dans le monde fonctionnel comme un "pli" (ou si vous êtes un théoricien pointu, un "catamorphisme"). Dans les langages fonctionnels, les types de données sont principalement des arbres ou des tuples, et chaque type d'arbre a un catamorphisme naturel qui lui est associé.

    Ces fonctions d'ordre supérieur s'accompagnent souvent de certaines lois de programmation, appelées "théorèmes libres".

  • Les programmeurs fonctionnels utilisent beaucoup moins de diagrammes que les programmeurs OO. Une grande partie de ce qui est exprimé dans les diagrammes OO est plutôt exprimée en types ou en «signatures», que vous devriez considérer comme des «types de modules». Haskell a également des "classes de type", qui sont un peu comme un type d'interface.

    Ces programmeurs fonctionnels qui utilisent des types pensent généralement que "une fois que vous avez trouvé les bons types, le code s’écrit pratiquement lui-même".

    Tous les langages fonctionnels n'utilisent pas de types explicites, mais le livre How To Design Programs , un excellent livre pour apprendre Scheme / Lisp / Clojure, s'appuie fortement sur des "descriptions de données", qui sont étroitement liées aux types.

Quelle est donc la méthodologie pour une conception systématique (basée sur un modèle?) D'une application fonctionnelle, c'est-à-dire en Lisp ou en Clojure?

Toute méthode de conception basée sur l'abstraction des données fonctionne bien. Je pense que c'est plus facile lorsque le langage a des types explicites, mais cela fonctionne même sans. Un bon livre sur les méthodes de conception pour les types de données abstraits, qui s'adapte facilement à la programmation fonctionnelle, est Abstraction and Specification in Program Development par Barbara Liskov et John Guttag, la première édition. Liskov a remporté le prix Turing en partie pour ce travail.

Une autre méthodologie de conception unique à Lisp consiste à décider quelles extensions de langage seraient utiles dans le domaine problématique dans lequel vous travaillez, puis à utiliser des macros hygiéniques pour ajouter ces constructions à votre langage. Un bon endroit pour lire sur ce type de conception est l'article de Matthew Flatt, Création de langues dans une raquette . L'article peut se trouver derrière un mur payant. Vous pouvez également trouver des informations plus générales sur ce type de conception en recherchant le terme «langage intégré spécifique au domaine»; pour des conseils et des exemples particuliers au-delà de ce que couvre Matthew Flatt, je commencerais probablement par Graham's On Lisp ou peut-être ANSI Common Lisp .

Quelles sont les étapes courantes, quels artefacts dois-je utiliser?

Étapes courantes:

  1. Identifiez les données de votre programme et les opérations qu'il contient, et définissez un type de données abstrait représentant ces données.

  2. Identifiez les actions ou les schémas de calcul courants et exprimez-les sous forme de fonctions ou de macros d'ordre supérieur. Attendez-vous à prendre cette étape dans le cadre de la refactorisation.

  3. Si vous utilisez un langage fonctionnel tapé, utilisez le vérificateur de type tôt et souvent. Si vous utilisez Lisp ou Clojure, la meilleure pratique consiste à écrire d'abord les contrats de fonction, y compris les tests unitaires - c'est un développement piloté par les tests au maximum. Et vous voudrez utiliser n'importe quelle version de QuickCheck qui a été portée sur votre plate-forme, qui dans votre cas ressemble à ce qu'on appelle ClojureCheck . C'est une bibliothèque extrêmement puissante pour construire des tests aléatoires de code qui utilise des fonctions d'ordre supérieur.

Norman Ramsey
la source
2
Le visiteur de l'OMI n'est pas plié - le pli est un sous-ensemble de visiteur. L'envoi multiple n'est pas (directement) capturé par le pli.
Michael Ekstrand
6
@Michael - en fait, vous pouvez capturer très soigneusement plusieurs envois avec différentes sortes de catamorphismes d'ordre supérieur. Le travail de Jeremy Gibbons est un endroit pour chercher cela, mais je recommanderais de travailler sur la programmation générique de type de données en général - j'aime particulièrement le papier de composition.
sclv
6
Je conviens que je vois des diagrammes utilisés beaucoup moins fréquemment pour décrire les conceptions fonctionnelles et je pense que c'est dommage. Il est certes difficile de représenter l'équivalent d'un diagramme de séquence lorsque vous utilisez beaucoup de HOF. Mais je souhaite que l'espace de la description des conceptions fonctionnelles avec des images soit mieux exploré. Autant je déteste UML (en tant que spécification), je trouve UML (en tant que croquis) très utile en Java et j'aimerais qu'il y ait de meilleures pratiques sur la façon de faire l'équivalent. J'ai essayé un peu de le faire avec des protocoles et des enregistrements Clojure, mais je n'ai rien de vraiment apprécié.
Alex Miller
22
+1 pour "Dieu merci, les ingénieurs en logiciel n'ont pas encore découvert la programmation fonctionnelle." ;)
Aky
1
OO est en soi une façon d'essayer de programmer avec des types, donc les approches ne sont pas si différentes. Le problème avec les conceptions OO semble généralement dériver du fait que les gens ne savent pas ce qu'ils font.
Marcin
46

Pour Clojure, je recommande de revenir à une bonne vieille modélisation relationnelle. Out of the Tarpit est une lecture inspirante.

cgrand
la source
C'est un excellent article, le bon vieux temps de l'informatique a dû être vraiment très bon, quand tous ces concepts ont survécu jusqu'à la renaissance d'aujourd'hui. C'est probablement dû aux solides bases en mathématiques.
Thorsten
1
Ce. CE. CE! Je lis ce document, et il est vraiment intéressant de voir comment il semble couvrir toutes les bases de ce qu'il faut pour construire de vrais systèmes, tout en maintenant un état mutable minimal de manière hautement contrôlée. Je joue avec la construction de Pong et Tetris dans un style FRelP (excusez l'étrange initialisme, mais il y a déjà un autre FRP plus populaire: Functional Reactive Programming).
John Cromartie
Après avoir lu l'article, je pense que clojure serait le langage parfait pour FR (el) P, au moins pour la logique essentielle , l' état et le contrôle accidentels et les autres composants. Je me demande comment faire une définition relationnelle de l' état essentiel en clojure sans réinventer sql (sans ses défauts)? Ou est-ce l'idée d'utiliser simplement une bonne base de données relationnelle (sql) et de construire un programme fonctionnel par-dessus sans l'inadéquation conceptuelle introduite par la POO?
Thorsten
1
@Thorsten l'idée de base est set = table, map = index. La partie difficile est de garder les index et les tables synchronisés, mais ce problème peut être résolu avec de meilleurs types de jeux. Un type d'ensemble simple que j'ai mis en œuvre est l'ensemble à clés, qui est un ensemble qui utilise une fonction clé pour tester l'unicité. Cela signifie que conjurer une insertion de valeur ou une mise à jour, appeler get avec les champs de clé primaire renvoie la ligne entière.
cgrand
38

Personnellement, je trouve que toutes les bonnes pratiques habituelles du développement OO s'appliquent également à la programmation fonctionnelle - juste avec quelques ajustements mineurs pour tenir compte de la vision du monde fonctionnelle. Du point de vue méthodologique, vous n'avez pas vraiment besoin de faire quelque chose de fondamentalement différent.

Mon expérience vient de mon passage de Java à Clojure ces dernières années.

Quelques exemples:

  • Comprendre votre domaine d'activité / modèle de données - tout aussi important si vous envisagez de concevoir un modèle d'objet ou de créer une structure de données fonctionnelle avec des cartes imbriquées. À certains égards, la PF peut être plus facile car elle vous encourage à penser le modèle de données séparément des fonctions / processus, mais vous devez toujours faire les deux.

  • Orientation du service dans la conception - fonctionne en fait très bien du point de vue de la PF, car un service typique n'est vraiment qu'une fonction avec quelques effets secondaires. Je pense que la vision «ascendante» du développement de logiciels parfois adoptée dans le monde Lisp n'est en fait que de bons principes de conception d'API orientés services sous un autre aspect.

  • Test Driven Development - fonctionne bien dans les langages FP, en fait parfois même mieux parce que les fonctions pures se prêtent très bien à l'écriture de tests clairs et reproductibles sans avoir besoin de mettre en place un environnement dynamique. Vous pouvez également vouloir créer des tests séparés pour vérifier l'intégrité des données (par exemple, cette carte contient-elle toutes les clés que j'attends, pour équilibrer le fait que dans un langage OO, la définition de classe appliquerait cela pour vous au moment de la compilation).

  • Prototying / itération - fonctionne tout aussi bien avec FP. Vous pourriez même être en mesure de créer des prototypes en direct avec des utilisateurs si vous êtes très doué pour créer des outils / DSL et les utiliser au REPL.

mikera
la source
3
Ces pratiques me semblent assez familières. Je pense toujours que quelqu'un devrait écrire l'équivalent fonctionnel de "Génie logiciel orienté objet utilisant UML, Patterns et Java" de Bruegge / Dutoit au lieu du sixième livre "Programing in Clojure". On pourrait l'appeler "Génie logiciel fonctionnel utilisant Clojure et ?? quoi ??". Utilisent-ils UML et des modèles dans FP? Je me souviens que Paul Graham a écrit que les modèles sont un signe d'un manque d'abstraction en Lisp, qui devrait être corrigé par l'introduction de nouvelles macros.
Thorsten
3
Mais si vous traduisez des modèles en meilleures pratiques, il peut également y avoir des modèles dans le monde de la FP, qui méritent d'être partagés avec les non initialisés.
Thorsten
2
Il y a une conception de principe intéressante dans le livre PAIP. norvig.com/paip.html
mathk
1
il existe également des modèles de programmation fonctionnels (schémas de récursivité, etc.)
Gabriel Ščerbák
13

La programmation OO couple étroitement les données avec le comportement. La programmation fonctionnelle sépare les deux. Vous n'avez donc pas de diagrammes de classes, mais vous avez des structures de données, et vous avez en particulier des types de données algébriques. Ces types peuvent être écrits pour correspondre très étroitement à votre domaine, notamment en éliminant les valeurs impossibles par construction.

Il n'y a donc pas de livres et de livres à ce sujet, mais il existe une approche bien établie pour, comme dit le proverbe, rendre les valeurs impossibles non représentables.

Ce faisant, vous pouvez faire une gamme de choix pour représenter à la place certains types de données en tant que fonctions, et inversement, représenter plutôt certaines fonctions comme une union de types de données afin que vous puissiez obtenir, par exemple, la sérialisation, des spécifications plus strictes, l'optimisation, etc. .

Ensuite, étant donné cela, vous écrivez des fonctions sur vos annonces de telle sorte que vous établissez une sorte d' algèbre - c'est-à-dire qu'il existe des lois fixes qui s'appliquent à ces fonctions. Certains sont peut-être idempotents - les mêmes après plusieurs applications. Certains sont associatifs. Certains sont transitifs, etc.

Vous avez maintenant un domaine sur lequel vous avez des fonctions qui composent selon des lois bien comportées. Une simple DSL embarquée!

Oh, et compte tenu des propriétés, vous pouvez bien sûr en écrire des tests aléatoires automatisés (ala QuickCheck) .. et ce n'est que le début.

sclv
la source
1
L'approche consistant à rendre les valeurs impossibles non représentables est moins applicable aux langues à typage dynamique comme Clojure et Scheme qu'aux langues à typage statique comme Haskell et ML.
Zak
@Zak - eh bien, vous ne pouvez pas vérifier statiquement qu'elles ne sont pas représentables, mais vous pouvez de toute façon créer vos structures de données de la même manière.
sclv
7

La conception orientée objet n'est pas la même chose que l'ingénierie logicielle. L'ingénierie logicielle concerne tout le processus de passage des exigences à un système opérationnel, dans les délais et avec un faible taux de défauts. La programmation fonctionnelle peut être différente de l'OO, mais elle ne supprime pas les exigences, les conceptions détaillées et de haut niveau, la vérification et les tests, les métriques logicielles, l'estimation, et tout ce "truc d'ingénierie logicielle".

De plus, les programmes fonctionnels présentent une modularité et d'autres structures. Vos conceptions détaillées doivent être exprimées en termes de concepts dans cette structure.

Kaz
la source
5

Une approche consiste à créer un DSL interne dans le langage de programmation fonctionnel de choix. Le "modèle" est alors un ensemble de règles commerciales exprimées dans la LIS.

James Kingsbery
la source
1
Je comprends l'approche pour construire d'abord le langage vers le domaine du problème jusqu'à ce qu'un niveau d'abstraction soit atteint qu'aucun motif répétitif ne se produise plus dans le code, que de résoudre le problème avec ces abstractions.
Thorsten
1
Mais à quoi ressemble-t-il lorsque "le modèle est un ensemble de règles commerciales exprimées dans la DSL"? Dans une application Java EE, le modèle est écrit en tant qu'entités POJO, qui sont appelées à partir d'EJB de contrôleur qui, à leur tour, mettent à jour les JSP de vue - par exemple. Existe-t-il des modèles architecturaux similaires (comme le modèle MVC) dans FP? À quoi ça ressemble?
Thorsten
2
Il n'y a aucune raison pour laquelle vous ne pouvez pas avoir de modèle MVC dans FP, précisément comme ça. FP vous permet toujours de créer des structures de données riches, et sans doute avec les ADT et la correspondance de modèles, vous permet de construire beaucoup plus riches . Si quoi que ce soit, puisque FP sépare les données et le comportement, les systèmes de type MVC apparaissent beaucoup plus naturellement.
sclv
5

Voir ma réponse à un autre post:

Comment Clojure aborde-t-il la séparation des préoccupations?

Je suis d'accord qu'il faut rédiger davantage sur le sujet sur la façon de structurer les grandes applications qui utilisent une approche FP (De plus, il faut en faire plus pour documenter les interfaces utilisateur pilotées par FP)

drcode
la source
3
J'aime le pipeline à 90% et l'approche macro à 10%. Il semble tout à fait naturel de considérer un programme fonctionnel comme un pipeline de transformations sur des données immuables. Je ne sais pas si je comprends ce que vous entendez par "mettre toute l'intelligence dans les données, pas le code", car l'approche consistant à avoir 100 fonctions travaillant sur 1 structure de données (plutôt que 10 fonctions sur 10 infrastructures de données) semble impliquer L'opposé. Les structures de données dans OOP ne sont-elles pas plus intelligentes que dans FP, car elles ont leur propre comportement intégré?
Thorsten
3

Bien que cela puisse être considéré comme naïf et simpliste, je pense que les "recettes de conception" (une approche systématique de la résolution de problèmes appliquée à la programmation comme le préconisent Felleisen et al. Dans leur livre HtDP ) seraient proches de ce que vous semblez rechercher.

Ici, quelques liens:

http://www.northeastern.edu/magazine/0301/programming.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371

Artyom Shalkhakov
la source
Le lien vers la page du nord-est semble mort.
James Kingsbery
1
James, tu as raison, et je ne me souviens pas de ce qui était là pour le réparer, malheureusement. Je sais seulement que les auteurs de HtDP ont continué à créer le langage Pyret (et probablement révisent la 2e édition de HtDP pour l'utiliser à la place de Racket, anciennement PLT Scheme).
Artyom Shalkhakov
3

J'ai récemment trouvé ce livre: Modélisation de domaine fonctionnelle et réactive

Je pense que cela correspond parfaitement à votre question.

De la description du livre:

La modélisation fonctionnelle et réactive de domaine vous apprend à penser le modèle de domaine en termes de fonctions pures et à les composer pour construire de plus grandes abstractions. Vous commencerez par les bases de la programmation fonctionnelle et progresserez progressivement vers les concepts et modèles avancés que vous devez connaître pour implémenter des modèles de domaine complexes. Le livre montre comment les modèles de FP avancés tels que les types de données algébriques, la conception basée sur les classes de types et l'isolement des effets secondaires peuvent faire en sorte que votre modèle se compose pour la lisibilité et la vérifiabilité.

elviejo79
la source
2

Il y a le style "calcul de programme" / "conception par calcul" associé au professeur Richard Bird et au groupe d'algèbre de programmation de l'Université d'Oxford (Royaume-Uni), je ne pense pas qu'il soit trop exagéré de considérer cela comme une méthodologie.

Personnellement, bien que j'aime le travail produit par le groupe AoP, je n'ai pas la discipline pour pratiquer le design de cette manière moi-même. Mais c'est ma lacune, et non celle du calcul du programme.

stephen tetley
la source
2

J'ai trouvé que le développement piloté par le comportement convenait naturellement au développement rapide de code dans Clojure et SBCL. Le véritable avantage de tirer parti de BDD avec un langage fonctionnel est que j'ai tendance à écrire des tests unitaires à grain beaucoup plus fin que d'habitude lorsque j'utilise des langages procéduraux, car je réussis beaucoup mieux à décomposer le problème en blocs de fonctionnalités plus petits.

Marc
la source
quels sont les outils que vous utilisez pour faire du BDD en clojure?
murtaza52
J'aime Midje. C'est à jour et très expressif. Découvrez-le: github.com/marick/Midje
Marc
1

Honnêtement, si vous voulez des recettes de conception pour des programmes fonctionnels, jetez un œil aux bibliothèques de fonctions standard telles que le Prélude de Haskell. Dans FP, les modèles sont généralement capturés par des procédures d'ordre supérieur (fonctions qui opèrent sur des fonctions) elles-mêmes. Donc, si un motif est vu, souvent une fonction d'ordre supérieur est simplement créée pour capturer ce motif.

Un bon exemple est fmap. Cette fonction prend une fonction comme argument et l'applique à tous les "éléments" du deuxième argument. Puisqu'il fait partie de la classe de type Functor, toute instance d'un Functor (comme une liste, un graphique, etc.) peut être passée comme deuxième argument à cette fonction. Il capture le comportement général de l'application d'une fonction à chaque élément de son deuxième argument.

nightki
la source
-7

Bien,

Généralement, de nombreux langages de programmation fonctionnelle sont utilisés dans les universités pendant longtemps pour les "problèmes de petits jouets".

Ils deviennent de plus en plus populaires maintenant, car la POO a des problèmes avec la "programmation parallèle" en raison de "l'état". Et parfois, le style fonctionnel est meilleur pour les problèmes actuels comme Google MapReduce.

Je suis sûr que, lorsque des gars functioanl frapperont le mur [essayez d'implémenter des systèmes de plus de 1.000.000 lignes de code], certains d'entre eux viendront avec de nouvelles méthodologies d'ingénierie logicielle avec des mots à la mode :-). Ils devraient répondre à la vieille question: comment diviser le système en morceaux afin que nous puissions «mordre» chaque morceau un par un? [travail itératif, inceremental et évolutif] utilisant le style fonctionnel.

Il est certain que le Style Fonctionnel affectera notre Style Orienté Objet. Nous "encore" de nombreux concepts des Systèmes Fonctionnels et adaptés à nos langages POO.

Mais les programmes fonctionnels seront-ils utilisés pour de si gros systèmes? Deviendront-ils le courant principal? Telle est la question .

Et personne ne peut venir avec une méthodologie réaliste sans implémenter de si gros systèmes, se salir les mains. Vous devez d'abord vous salir les mains, puis suggérer une solution. Solutions-Suggestions sans "vraies douleurs et saletés" seront "fantastiques".

Hippias Minor
la source
Il y a déjà eu suffisamment de systèmes à grande échelle construits avec des langages fonctionnels. Même s'il n'y en avait pas, ce n'est pas du tout un argument.
Svante
Eh bien, en nommer certains? Je connais très peu de systèmes "Erlang". [taille moyenne] Mais Haskel? Clojure? Zézayer?
Hippias Minor
Et que [écrire de grands systèmes] est le véritable argument. Parce que c'est le cas de test. Ce cas de test montre que si ce style fonctionnel est utile et peut-on faire des choses pratiques avec dans le monde réel.
Hippias Minor
2
Ce qui est drôle au sujet des langues qui ne sont pas «OOP» anales, c'est qu'elles vous laissent souvent à l'abri des «méthodologies de conception», de penser par vous-même et de couper votre programme de la manière la plus appropriée, au lieu de suivre aveuglément un modèle défini et de vivre avec le passe-partout bureaucratique. Désolé, aucun cours de 10 semaines de 3 points ici.
Svante
1
J'ai vu des choses auxquelles tu ne croirais pas.
Svante