À grande échelle de conception en Haskell?
Ce qui est un bon moyen de conception/structure de grands programmes fonctionnels, en particulier en Haskell?
J'ai été à travers un tas de tutoriels (Écrire Vous-même un Régime d'être mon préféré, avec Real World Haskell une seconde près) mais la plupart de ces programmes sont relativement petits, et à usage unique. En outre, je ne considère pas certains d'entre eux à être particulièrement élégante (par exemple, la grande tables de recherche dans des VOIES).
Je suis en train de vouloir écrire des programmes plus importants, avec plus de pièces mobiles, - l'acquisition des données à partir d'une variété de sources différentes, le nettoyage, le traitement de différentes façons, en affichant dans les interfaces utilisateur, de conservation, de communication sur les réseaux, etc. Comment pourrait-on mieux la structure du code pour être lisible, maintenable, et adaptable à l'évolution des besoins?
Il n'y a bien un grand de la littérature traitant de ces questions pour les grandes orientée objet de programmes impératifs. Des idées comme MVC, design patterns, etc. sont décents prescriptions pour la réalisation des objectifs généraux tels que la séparation des préoccupations et de la possibilité de réutilisation dans un OO style. En outre, de nouveaux langages impératifs, se prêtent à une "conception de l'as you grow" style de refactoring pour laquelle, dans mon novice avis, Haskell semble moins bien adapté.
Est-il un équivalent de la littérature pour Haskell? Comment est le zoo exotique des structures de contrôle disponibles dans la programmation fonctionnelle (les monades, des flèches, des applicatifs, etc.) mieux employées à cette fin? Quelles sont les meilleures pratiques pouvez-vous recommander?
Merci!
MODIFIER (ce qui est un suivi de Don Stewart réponse):
@enfile mentionné: "les Monades touche de capture des conceptions architecturales dans les types de."
Je suppose que ma question est: comment doit-on penser clé de la conception architecturale dans un langage purement fonctionnel?
Prenons l'exemple de plusieurs flux de données, et un traitement en plusieurs étapes. Je peux écrire modulaire des analyseurs syntaxiques pour le flux de données à un ensemble de structures de données, et je peux mettre en œuvre chaque étape de transformation par une fonction pure. Les étapes de traitement nécessaires pour un morceau de données dépendra de sa valeur et de celle des autres. Certaines des mesures devraient être suivies d'effets secondaires comme GUI mises à jour ou des requêtes de base de données.
Quelle est la "bonne" façon de lier les données et l'analyse des étapes d'une belle manière? On pourrait écrire une grande fonction qui fait la bonne chose pour les différents types de données. Ou on peut utiliser un monade de garder une trace de ce qui a été traitée jusqu'à présent et demandez à chaque étape de la transformation obtenir ce dont il a besoin à côté de la monade de l'état. Ou on pourrait écrire en grande partie des programmes et envoyer des messages autour (je n'aime pas beaucoup cette option).
Les diapositives qu'il a lié avoir un les Choses, nous avons Besoin de puce: "les expressions idiomatiques pour la cartographie de la conception sur le
types/fonctions/classes/monades". Quelles sont les expressions idiomatiques? 🙂
- Je pense que l'idée de base lors de l'écriture de grands programmes dans un langage fonctionnel est petit, spécialisés, les apatrides et les modules de communication par passage de messages. Bien sûr, vous avez à faire semblant un peu parce qu'un vrai programme besoins de l'état. Je pense que c'est là F# brille au-dessus de Haskell.
- mais seulement Haskell applique l'apatridie par défaut.Vous n'avez pas le choix, et doivent travailler dur pour introduire de l'état (à briser compositionnalité) en Haskell 🙂
- Ouais je sais, mais je fais partie de ce meilleur des deux mondes type de gars.
- Au premier abord, il ressemble à beaucoup de travail, mais vous seriez surpris de voir à quel point il est largement le cadre de différents programmes de l'envoi de messages.
- Je ne suis pas en désaccord, en théorie. Certes, dans un langage impératif (ou un autre fonctionnel conçu autour de transmission de messages), qui pourrait très bien être ce que j'ai fais. Mais Haskell a d'autres façons de composer avec l'état, et peut-être ils me permettent de garder plus de la "pure" des avantages.
- J'ai écrit un peu à ce sujet sous "Design Guidelines" dans ce document: community.haskell.org/~ndm/téléchargements/...
- "Ce qui est une bonne manière de la conception ou de la structure de grands programmes fonctionnels, en particulier en Haskell?". Dans sa présentation, "de l'Ingénierie de Grands Projets en Haskell", Don Stewart dit de Galois ont développé jusqu'à 0,2 MLOC Haskell programmes, qui est minuscule par rapport aux normes modernes (un grand nombre des plus grands du monde des bases de code sont maintenant plus de 10MLOC!). Étant donné que de Galois sont à peu près les seuls grands industriels de l'utilisateur de Haskell dans le monde, je dirais que personne n'a jamais développé un grand Haskell programme ainsi personne ne peut savoir comment structurer un.
- Notez que vous pouvez faire état dans haskell avec une récursion infinie ou monades par exemple.
- n'oublions pas que tout MLOC est une bonne mesure lorsque vous comparez des projets en langues similaires, il n'a pas beaucoup de sens pour la croix-langue de comparaison, en particulier avec des langages comme Haskell, où la réutilisation du code et de la modularité est beaucoup plus facile & coffre-fort par rapport à certaines langues.
- comme Haskell, où la réutilisation du code et de la modularité est beaucoup plus facile & safe". Je trouve que l'énoncé bizarre quand Haskell n'a qu'un vestige de module de système par rapport à ML. Pourquoi il y a au moins deux >1MLOC code OCaml bases dans l'industrie (Jeanne de Saint-laurent et Citrix), mais pas de Haskell ceux?
- Je ne voulais pas dire 'modularité", comme en Java, il n'était plus sur de ne pas avoir besoin de beaucoup colle le code ou l'optimisation des structures de données lors de l'utilisation de bibliothèques. Peut-être "modularité" n'est pas le mot juste ici. Je n'ai pas de GROS projets en haskell, mais j'ai toujours été étonné de voir comment peu de code, je me retrouve avec quand je sais qu'une bibliothèque probablement gère ses tâches de manière efficace. Je pense que, la plupart de ceci vient de la paresse, de lang. Est OCaml paresseux?
- OCaml n'est pas paresseux. J'ai étudié plusieurs OSS Haskell bases de code et ne pense pas que la paresse a semblé particulièrement avantageux.
- Pouvez-vous élaborer sur la façon dont "les monades touche de capture des conceptions architecturales dans les types"?
- Pourquoi beaucoup de vraiment bonnes questions sont-elles fermées?
- Matt Parsons a écrit un intéressant billet de blog sur la conception de l'application en Haskell: parsonsmatt.org/2018/03/22/three_layer_haskell_cake.html
Vous devez vous connecter pour publier un commentaire.
Je parle un peu à ce sujet dans L'ingénierie de Grands Projets en Haskell et dans le La conception et la mise en Œuvre de XMonad. d'Ingénierie dans le grand est à propos de la gestion de la complexité. Le code principal de la structuration des mécanismes en Haskell pour la gestion de la complexité sont:
Le système de type
Le profiler
Pureté
Tests
Monades pour la Structuration de
Type de classes et existentielle types
Concurrence et parallélisme
par
dans votre programme de battre la concurrence avec facile, composable parallélisme.Refactoriser
Utiliser les FFI à bon escient
Méta-programmation
De l'emballage et de la distribution
Avertissements
-Wall
pour garder votre code propre des odeurs. Vous pouvez également regarder Agda, Isabelle ou de Capture pour plus d'assurance. Les peluches-comme la vérification, voir la grande hlint, qui proposera des améliorations.Avec tous ces outils, vous pouvez garder une poignée sur la complexité, la suppression plus grand nombre d'interactions entre les composants que possible. Idéalement, vous avez une très grosse base de code pur, qui est très facile à entretenir, car il est de composition. Ce n'est pas toujours possible, mais il vaut la peine de viser.
En général: décomposer les unités logiques de votre système dans le plus petit referentially composants transparents possible, puis de les mettre en œuvre dans les modules. Global ou local environnements pour les ensembles de composants (ou à l'intérieur des composants) peuvent être mappés à des monades. Utilisation des types de données algébriques pour décrire les structures de données de base. Partager ces définitions largement.
Ne vous a donné plus de détails ci-dessus, mais voici mes deux cents de faire vraiment nitty-gritty dynamique des programmes comme les démons système en Haskell.
En fin de compte, vous vivez dans une monade transformateur de la pile. En bas, c'est IO. Ci-dessus que, dans tous les grands module (dans le sens abstrait, et non pas le module-dans-un-fichier de sens, les cartes son état dans un calque dans la pile. Donc si vous avez votre connexion de base de données de code caché dans un module, vous écrire tout cela à plus d'un type de MonadReader m Connexion => ... -> m ... et puis vos fonctions de base de données peut toujours obtenir leur connexion sans fonctions à partir d'autres modules d'avoir à être conscient de son existence. Vous pourriez vous retrouver avec une couche de transport de votre connexion de base de données, un autre de votre configuration, un tiers de vos différents sémaphores et mvars pour la résolution de parallélisme et la synchronisation, un autre de votre fichier de log poignées, etc.
Comprendre votre erreur de manipulation première. La plus grande faiblesse au moment de Haskell dans les grands systèmes est la pléthore de méthodes de gestion des erreurs, y compris moche comme Peut-être (ce qui est un tort parce que vous ne pouvez pas retourner toute information sur ce qui s'est passé; toujours utiliser au lieu de Peut-être, sauf si vous avez vraiment seulement la moyenne des valeurs manquantes). Comprendre comment vous allez faire en premier, et configurer les adaptateurs de divers mécanismes de gestion des erreurs de vos bibliothèques et autres code utilise dans votre finale. Cela vous permettra d'économiser un monde de chagrin plus tard.
Additif (extrait du commentaires; grâce à Lii & liminalisht) —
plus de discussion sur les différentes façons de couper un gros programme en monades dans une pile:
Ben De La Teinture donne une grande pratique de l'intro à ce sujet, et Brian Mal traite des solutions au problème de la
lift
ing monadique des actions dans votre personnalisé monade. George Wilson montre comment utilisermtl
d'écrire du code qui fonctionne avec n'importe quel monade qui implémente le typeclasses, plutôt que de votre personnalisé monade genre. Carlo Hamalainen a écrit quelques courtes, des notes utiles, résumant George parler.lift
ing monadique des actions dans votre personnalisé monade. George Wilson montre comment utilisermtl
d'écrire du code qui fonctionne avec n'importe quel monade qui implémente le typeclasses, plutôt que de votre personnalisé monade genre. Carlo Hamalainen a écrit quelques courtes, des notes utiles, résumant George parler.De la conception de grands programmes en Haskell n'est pas différent de le faire dans d'autres langues.
Programmation dans le grand est de la rupture de votre problème en morceaux maniables, et la façon de les intégrer ensemble; la mise en œuvre de la langue est moins important.
Cela dit, dans une conception large, il est agréable d'essayer et de l'effet de levier le type de système pour vous assurer que vous ne pouvez l'adapter à votre morceaux ensemble dans une manière qui est correct. Cela peut impliquer newtype ou types fantômes à faire des choses qui semblent avoir le même type différent.
Quand il s'agit de refactoriser le code comme vous allez le long, la pureté est une aubaine, donc, essayez de garder autant de code que possible pur. Pur code est facile à refactoriser, parce qu'il n'a pas caché interaction avec d'autres parties de votre programme.
J'ai appris structuré la programmation fonctionnelle, la première fois avec ce livre.
Il peut ne pas être exactement ce que vous cherchez, mais pour les débutants en programmation fonctionnelle, cela peut être l'un des meilleurs premiers pas pour apprendre à structurer des programmes fonctionnels - indépendante de l'échelle. Sur tous les niveaux d'abstraction, le design doit toujours claire des structures.
L'art de la Programmation Fonctionnelle
http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/
where beginner=do write $ tutorials `about` Monads
)Je suis en train d'écrire un livre avec le titre "Design Fonctionnel et de l'Architecture". Il vous offre un ensemble complet de techniques comment construire une grande application à l'aide de la pure approche fonctionnelle. Il décrit de nombreux schémas fonctionnels et des idées, tandis que la construction d'un SCADA-comme l'application "Andromède" pour le contrôle de vaisseaux spatiaux à partir de zéro. Ma première langue est l'Haskell. Le livre couvre:
Vous pouvez vous familiariser avec le code de la livre ici, et la 'Andromède' projet de code.
J'attends de finir ce livre à la fin de 2017. En attendant, vous pouvez lire mon article "le Design et l'Architecture dans la Programmation Fonctionnelle" (Rus) ici.
Mise à JOUR
J'ai partagé mon book en ligne (5 premiers chapitres). Voir post sur Reddit
Gabriel blog Évolutive programme d'architectures peut-être mérite une mention.
Souvent, il me semble qu'apparemment une architecture élégante qui a souvent tendance à tomber sur les bibliothèques qui présentent ce sentiment agréable d'homogénéité, dans une sorte de façon. En Haskell c'est particulièrement évident - modèles qui serait normalement considéré comme "top-down " de l'architecture" ont tendance à être capturé dans les bibliothèques comme mvc, Netwire et Cloud Haskell. C'est-à-dire, j'espère que cette réponse ne sera pas interprété comme une tentative de remplacer l'un quelconque des autres dans ce fil, il suffit que les choix structurels peuvent et doivent idéalement être disparaît dans les bibliothèques par des experts du domaine. La vraie difficulté dans la construction de grands systèmes, à mon avis, est l'évaluation de ces bibliothèques sur leur architectural "bonté" par rapport à l'ensemble de vos soucis pragmatiques.
Comme liminalisht mentionne dans les commentaires, La catégorie modèle de conception est un autre poste par Gabriel sur le sujet, dans une veine similaire.
J'ai trouvé le papier "Logiciel d'Enseignement de l'Architecture à l'Aide de Haskell" (pdf) par Alejandro Serrano utile pour la réflexion sur la structure à grande échelle Haskell.
Vous avez peut-être aller un pas en arrière et de penser à la façon de traduire la description du problème à une conception en premier lieu. Depuis Haskell est donc de haut niveau, il peut saisir la description du problème dans la forme de structures de données , les actions que les procédures et la pure transformation sous forme de fonctions. Ensuite, vous avez une conception. Le début du développement lors de la compilation de ce code et de trouver béton erreurs sur les champs manquants, manquant instances et manquant monadique transformateurs dans votre code, par exemple parce que vous effectuez une base de données Access à partir d'une bibliothèque qui ont besoin d'un certain état de monade dans un IO de procédure. Et voila, il y a le programme. Le compilateur nourrir votre mental croquis et donne de la cohérence à la conception et le développement.
De cette manière, vous bénéficiez de l'aide de Haskell depuis le début, et le codage est naturel. Je ne voudrais pas le soin de faire quelque chose de "fonctionnelle" ou "pure" ou assez général, même si ce que vous avez à l'esprit est un béton ordinaire problème. Je pense que le génie est la chose la plus dangereuse en ELLE. Les choses sont différentes lorsque le problème est de créer une bibliothèque abstraite d'un ensemble de problèmes liés à l'.