Quelle est une bonne façon de concevoir/structurer de grands programmes fonctionnels, notamment en Haskell ?
J'ai parcouru un certain nombre de tutoriels (Write Yourself a Scheme étant mon préféré, avec Real World Haskell en deuxième position) - mais la plupart des programmes sont relativement petits et à usage unique. De plus, je ne considère pas que certains d'entre eux soient particulièrement élégants (par exemple, les vastes tables de consultation dans WYAS).
J'ai maintenant envie d'écrire des programmes plus importants, avec plus de pièces mobiles - acquérir des données à partir d'une variété de sources différentes, les nettoyer, les traiter de diverses manières, les afficher dans des interfaces utilisateur, les persister, communiquer sur des réseaux, etc. Comment structurer au mieux un tel code pour qu'il soit lisible, maintenable et adaptable à l'évolution des besoins ?
Il existe une littérature assez importante traitant de ces questions pour les grands programmes impératifs orientés objet. Des idées comme MVC, les modèles de conception, etc. sont des prescriptions décentes pour réaliser des objectifs généraux comme la séparation des préoccupations et la réutilisabilité dans un style OO. De plus, les langages impératifs les plus récents se prêtent à un style de refactoring "design as you grow" auquel, selon mon opinion de novice, Haskell semble moins bien adapté.
Existe-t-il une littérature équivalente pour Haskell ? Comment utiliser au mieux le zoo de structures de contrôle exotiques disponibles dans la programmation fonctionnelle (monades, flèches, applicatifs, etc.) ? Quelles sont les meilleures pratiques que vous pourriez recommander ?
Gracias.
EDIT (ceci est un suivi de la réponse de Don Stewart) :
@dons a mentionné : "Les monades capturent des conceptions architecturales clés dans les types."
Je suppose que ma question est la suivante : comment penser aux conceptions architecturales clés dans un langage fonctionnel pur ?
Prenons l'exemple de plusieurs flux de données et de plusieurs étapes de traitement. Je peux écrire des analyseurs modulaires pour les flux de données vers un ensemble de structures de données, et je peux implémenter chaque étape de traitement comme une fonction pure. Les étapes de traitement requises pour un élément de données dépendront de sa valeur et de celle des autres. Certaines de ces étapes doivent être suivies d'effets secondaires tels que des mises à jour de l'interface graphique ou des requêtes de base de données.
Quelle est la "bonne" façon de lier les données et les étapes d'analyse syntaxique d'une manière agréable ? On pourrait écrire une grande fonction qui fait la bonne chose pour les différents types de données. Ou on pourrait utiliser une monade pour garder la trace de ce qui a été traité jusqu'à présent et faire en sorte que chaque étape de traitement obtienne ce dont elle a besoin à partir de l'état de la monade. On peut aussi écrire des programmes largement séparés et envoyer des messages (je n'aime pas trop cette option).
Les diapositives qu'il a liées ont une puce "Things we Need" : "Idiomes pour le mappage de la conception sur types/fonctions/classes/monades". Quels sont ces idiomes ? :)
9 votes
Je pense que l'idée principale quand on écrit de gros programmes dans un langage fonctionnel est de petits modules spécialisés et apatrides communiquant par passage de messages . Bien sûr, il faut faire un peu semblant car un vrai programme a besoin d'un état. Je pense que c'est là que F# brille par rapport à Haskell.
18 votes
Vous n'avez pas le choix, et vous devez travailler dur pour introduire un état (pour briser la compositionnalité) en Haskell :-)
0 votes
@Don - Oui, je sais, mais je suis l'un de ces types qui ont le meilleur des deux mondes.
0 votes
@caffeine - Au début, cela semble être beaucoup de travail mais vous seriez surpris de voir à quel point c'est génial d'avoir des programmes largement séparés qui envoient des messages.
7 votes
@ChaosPandion : Je ne suis pas en désaccord, en théorie. Certainement, dans un langage impératif (ou un langage fonctionnel conçu autour du passage de messages), cela pourrait très bien être ce que je ferais. Mais Haskell a d'autres moyens de gérer l'état, et peut-être qu'ils me permettent de garder plus d'avantages "purs".
1 votes
J'ai écrit un peu sur ce sujet dans la rubrique "Lignes directrices de conception" de ce document : community.haskell.org/~ndm/downloads/
0 votes
"Quelle est la bonne façon de concevoir/structurer de grands programmes fonctionnels, notamment en Haskell ?". Dans sa présentation "Engineering Large Projects in Haskell", Don Stewart dit que Galois a développé jusqu'à 0.2MLOC de programmes Haskell, ce qui est minuscule selon les standards modernes (beaucoup des plus grandes bases de code du monde dépassent maintenant 10MLOC !) Étant donné que Galois est à peu près le seul utilisateur industriel majeur de Haskell dans le monde, je dirais que personne n'a jamais développé un grand programme Haskell et que personne ne saura donc comment en structurer un.
0 votes
@ChaosPandion Notez que vous pouvez faire de l'état dans haskell avec une récursion infinie ou des monades par exemple.
5 votes
N'oublions pas que si MLOC est une bonne mesure pour comparer des projets dans des langages similaires, elle n'a pas beaucoup de sens pour une comparaison inter-langues, surtout avec des langages comme Haskell, où la réutilisation du code et la modularité sont beaucoup plus faciles et sûres par rapport à d'autres langages.
1 votes
@tair : "des langages comme Haskell, où la réutilisation du code et la modularité sont beaucoup plus faciles et sûres". Je trouve cette affirmation étrange alors que Haskell n'a qu'un système de modules vestigial comparé à ML. Pourquoi y a-t-il au moins deux bases de code OCaml >1MLOC dans l'industrie (Jane St. et Citrix) mais aucune en Haskell ?
1 votes
Je ne parlais pas de " modularité " comme dans Java, mais plutôt de ne pas avoir besoin de beaucoup de code " glue " ou d'optimiser les structures de données en utilisant des bibliothèques. Peut-être que "modularité" n'est pas le bon mot ici. Je n'ai pas eu de GROS projets en Haskell, mais j'ai toujours été étonné de voir le peu de code que j'avais quand je savais qu'une bibliothèque gérait probablement ses tâches de la manière la plus efficace. Je pense que la plupart de ceci vient de la paresse du langage. OCaml est-il paresseux ?
0 votes
@tair : OCaml n'est pas paresseux. J'ai étudié plusieurs bases de code OSS Haskell et la paresse ne m'a pas semblé particulièrement avantageuse.
1 votes
Pouvez-vous expliquer comment "les monades capturent des conceptions architecturales clés dans les types" ?
0 votes
Pourquoi beaucoup de bonnes questions sont-elles fermées ?
0 votes
Matt Parsons a écrit un article de blog intéressant sur la conception d'applications en Haskell : parsonsmatt.org/2018/03/22/gateau_haskell_trois_couches.html