Großes Design in Haskell? [geschlossen]


565

Was ist ein guter Weg, um große Funktionsprogramme zu entwerfen / strukturieren, insbesondere in Haskell?

Ich habe eine Reihe von Tutorials durchlaufen (Write Yourself a Scheme ist mein Favorit, Real World Haskell steht an zweiter Stelle) - aber die meisten Programme sind relativ klein und für einen bestimmten Zweck bestimmt. Darüber hinaus halte ich einige von ihnen nicht für besonders elegant (zum Beispiel die umfangreichen Nachschlagetabellen in WYAS).

Ich möchte jetzt größere Programme mit beweglicheren Teilen schreiben - Daten aus verschiedenen Quellen erfassen, bereinigen, auf verschiedene Arten verarbeiten, in Benutzeroberflächen anzeigen, beibehalten, über Netzwerke kommunizieren usw. Wie könnte Eine beste Struktur, um solchen Code lesbar, wartbar und an sich ändernde Anforderungen anpassbar zu machen?

Es gibt eine ziemlich große Literatur, die sich mit diesen Fragen für große objektorientierte imperative Programme befasst. Ideen wie MVC, Entwurfsmuster usw. sind anständige Vorschriften zur Verwirklichung umfassender Ziele wie der Trennung von Bedenken und der Wiederverwendbarkeit in einem OO-Stil. Darüber hinaus eignen sich neuere imperative Sprachen für einen Refactoring-Stil, für den Haskell meiner Meinung nach weniger geeignet ist.

Gibt es eine gleichwertige Literatur für Haskell? Wie wird der Zoo exotischer Kontrollstrukturen, die in der funktionalen Programmierung verfügbar sind (Monaden, Pfeile, Applikative usw.), für diesen Zweck am besten eingesetzt? Welche Best Practices können Sie empfehlen?

Vielen Dank!

EDIT (dies ist eine Fortsetzung der Antwort von Don Stewart):

@dons erwähnt: "Monaden erfassen wichtige architektonische Entwürfe in Typen."

Ich denke meine Frage ist: Wie sollte man über wichtige architektonische Entwürfe in einer reinen funktionalen Sprache denken?

Betrachten Sie das Beispiel mehrerer Datenströme und mehrerer Verarbeitungsschritte. Ich kann modulare Parser für die Datenströme in eine Reihe von Datenstrukturen schreiben und jeden Verarbeitungsschritt als reine Funktion implementieren. Die für ein Datenelement erforderlichen Verarbeitungsschritte hängen von seinem Wert und dem anderer Daten ab. Auf einige der Schritte sollten Nebenwirkungen wie GUI-Updates oder Datenbankabfragen folgen.

Was ist der richtige Weg, um die Daten und die Parsing-Schritte auf nette Weise zu verknüpfen? Man könnte eine große Funktion schreiben, die für die verschiedenen Datentypen das Richtige tut. Oder man könnte eine Monade verwenden, um zu verfolgen, was bisher verarbeitet wurde, und jeden Verarbeitungsschritt vom Monadenstatus alles bekommen zu lassen, was er als nächstes benötigt. Oder man könnte weitgehend separate Programme schreiben und Nachrichten senden (diese Option gefällt mir nicht besonders).

Die Folien, die er verlinkt hat, haben eine Aufzählungszeichen: "Redewendungen für die Zuordnung von Design zu Typen / Funktionen / Klassen / Monaden". Was sind die Redewendungen? :) :)


9
Ich denke, die Kernidee beim Schreiben großer Programme in einer funktionalen Sprache sind kleine, spezialisierte und zustandslose Module, die über die Nachrichtenübermittlung kommunizieren . Natürlich muss man ein bisschen so tun, als ob ein echtes Programm einen Zustand braucht. Ich denke, hier scheint F # über Haskell.
ChaosPandion

18
@Chaos, aber nur Haskell erzwingt standardmäßig Staatenlosigkeit. Sie haben keine Wahl und müssen hart arbeiten, um den Zustand (um die Komposition zu brechen) in Haskell einzuführen :-)
Don Stewart

7
@ChaosPandion: Theoretisch bin ich nicht anderer Meinung. In einer zwingenden Sprache (oder einer funktionalen Sprache, die sich mit dem Weiterleiten von Nachrichten befasst) könnte ich das durchaus tun. Aber Haskell hat andere Möglichkeiten, mit dem Staat umzugehen, und vielleicht lassen sie mich mehr von den "reinen" Vorteilen behalten.
Dan

1
Ich habe ein wenig darüber unter "Design Guidelines" in diesem Dokument geschrieben: community.haskell.org/~ndm/downloads/…
Neil Mitchell

5
@JonHarrop Vergessen wir nicht, dass MLOC zwar eine gute Metrik ist, wenn Sie Projekte in ähnlichen Sprachen vergleichen, aber für den sprachübergreifenden Vergleich wenig Sinn macht, insbesondere mit Sprachen wie Haskell, in denen die Wiederverwendung und Modularität von Code viel einfacher und sicherer ist im Vergleich zu einigen Sprachen da draußen.
Tair

Antworten:


519

Ich spreche ein wenig darüber in Engineering Large Projects in Haskell und im Design und der Implementierung von XMonad. Beim Engineering im Großen und Ganzen geht es um das Management von Komplexität. Die primären Codestrukturierungsmechanismen in Haskell zur Verwaltung der Komplexität sind:

Das Typsystem

  • Verwenden Sie das Typsystem, um Abstraktionen zu erzwingen und Interaktionen zu vereinfachen.
  • Erzwingen Sie Schlüsselinvarianten über Typen
    • (zB dass bestimmte Werte einem gewissen Bereich nicht entgehen können)
    • Dieser bestimmte Code führt keine E / A aus und berührt die Festplatte nicht
  • Sicherheit erzwingen: geprüfte Ausnahmen (Vielleicht / Entweder), Vermischungskonzepte (Word, Int, Adresse) vermeiden
  • Gute Datenstrukturen (wie Reißverschlüsse) können einige Testklassen unnötig machen, da sie z. B. statische Fehler außerhalb der Grenzen ausschließen.

Der Profiler

  • Geben Sie objektive Beweise für die Heap- und Zeitprofile Ihres Programms.
  • Insbesondere die Heap-Profilerstellung ist der beste Weg, um keine unnötige Speichernutzung sicherzustellen.

Reinheit

  • Reduzieren Sie die Komplexität drastisch, indem Sie den Status entfernen. Rein funktionaler Code skaliert, weil er kompositorisch ist. Alles, was Sie brauchen, ist der Typ, der bestimmt, wie Code verwendet wird - er wird nicht auf mysteriöse Weise unterbrochen, wenn Sie einen anderen Teil des Programms ändern.
  • Verwenden Sie viele Programme im Stil "Modell / Ansicht / Controller": Analysieren Sie externe Daten so schnell wie möglich in rein funktionale Datenstrukturen, bearbeiten Sie diese Strukturen und rendern / spülen / serialisieren Sie sie, sobald alle Arbeiten abgeschlossen sind. Hält den größten Teil Ihres Codes rein

Testen

  • QuickCheck + Haskell Code Coverage, um sicherzustellen, dass Sie die Dinge testen, die Sie nicht mit Typen überprüfen können.
  • GHC + RTS ist ideal, um festzustellen, ob Sie zu viel Zeit mit GC verbringen.
  • Mit QuickCheck können Sie auch saubere, orthogonale APIs für Ihre Module identifizieren. Wenn die Eigenschaften Ihres Codes schwer anzugeben sind, sind sie wahrscheinlich zu komplex. Fahren Sie mit dem Refactoring fort, bis Sie über einen sauberen Satz von Eigenschaften verfügen, mit denen Sie Ihren Code testen können und die sich gut zusammensetzen lassen. Dann ist der Code wahrscheinlich auch gut gestaltet.

Monaden zur Strukturierung

  • Monaden erfassen wichtige Architekturentwürfe in Typen (dieser Code greift auf Hardware zu, dieser Code ist eine Einzelbenutzersitzung usw.)
  • ZB erfasst die X-Monade in xmonad genau das Design für welchen Zustand für welche Komponenten des Systems sichtbar ist.

Typklassen und existenzielle Typen

  • Verwenden Sie Typklassen, um Abstraktion bereitzustellen: Verstecken Sie Implementierungen hinter polymorphen Schnittstellen.

Parallelität und Parallelität

  • Schleichen Sie sich parin Ihr Programm ein, um die Konkurrenz mit einfacher, komponierbarer Parallelität zu schlagen.

Refactor

  • Sie können in Haskell viel umgestalten . Die Typen stellen sicher, dass Ihre umfangreichen Änderungen sicher sind, wenn Sie Typen mit Bedacht einsetzen. Dies hilft Ihrer Codebasis-Skalierung. Stellen Sie sicher, dass Ihre Refactorings bis zum Abschluss Typfehler verursachen.

Verwenden Sie den FFI mit Bedacht

  • Das FFI erleichtert das Spielen mit Fremdcode, aber dieser Fremdcode kann gefährlich sein.
  • Seien Sie sehr vorsichtig bei Annahmen über die Form der zurückgegebenen Daten.

Meta-Programmierung

  • Ein bisschen Template Haskell oder Generika können Boilerplate entfernen.

Verpackung und Vertrieb

  • Benutze Cabal. Rollen Sie nicht Ihr eigenes Build-System. (BEARBEITEN: Eigentlich möchten Sie Stack jetzt wahrscheinlich verwenden, um loszulegen .)
  • Verwenden Sie Haddock für gute API-Dokumente
  • Tools wie graphmod können Ihre Modulstrukturen anzeigen .
  • Verlassen Sie sich nach Möglichkeit auf die Haskell Platform-Versionen von Bibliotheken und Tools. Es ist eine stabile Basis. (BEARBEITEN: Auch heutzutage möchten Sie wahrscheinlich Stack verwenden , um eine stabile Basis zum Laufen zu bringen.)

Warnungen

  • Verwenden -WallSie diese Option , um Ihren Code geruchsfrei zu halten. Sie können sich auch Agda, Isabelle oder Catch ansehen, um mehr Sicherheit zu erhalten. Informationen zur fusselartigen Überprüfung finden Sie in der großen Anleitung , die Verbesserungen vorschlägt.

Mit all diesen Tools können Sie die Komplexität im Griff behalten und so viele Interaktionen zwischen Komponenten wie möglich entfernen. Idealerweise verfügen Sie über eine sehr große Basis an reinem Code, der sehr einfach zu pflegen ist, da er kompositorisch ist. Das ist nicht immer möglich, aber es lohnt sich, darauf zu zielen.

Im Allgemeinen: Zerlegen Sie die logischen Einheiten Ihres Systems in die kleinstmöglichen referenziell transparenten Komponenten und implementieren Sie sie dann in Modulen. Globale oder lokale Umgebungen für Komponentensätze (oder innerhalb von Komponenten) können Monaden zugeordnet werden. Verwenden Sie algebraische Datentypen, um Kerndatenstrukturen zu beschreiben. Teilen Sie diese Definitionen weit.


8
Danke Don, deine Antwort ist ausgezeichnet - das sind alles wertvolle Richtlinien und ich werde regelmäßig darauf verweisen. Ich denke, meine Frage tritt einen Schritt auf, bevor man all dies brauchen würde. Was ich wirklich gerne wissen würde, sind die "Redewendungen für die Zuordnung von Design zu Typen / Funktionen / Klassen / Monaden" ... Ich könnte versuchen, meine eigenen zu erfinden, aber ich hatte gehofft, dass es irgendwo eine Reihe von Best Practices geben könnte - oder wenn nicht, Empfehlungen für gut strukturierten Code zum Lesen eines großen Systems (im Gegensatz beispielsweise zu einer fokussierten Bibliothek). Ich habe meinen Beitrag bearbeitet, um dieselbe Frage direkter zu stellen.
Dan

6
Ich habe Modulen Text zur Zerlegung von Design hinzugefügt. Ihr Ziel ist es, logisch verwandte Funktionen in Modulen zu identifizieren, die referenziell transparente Schnittstellen zu anderen Teilen des Systems haben, und so schnell wie möglich rein funktionale Datentypen zu verwenden, um die Außenwelt sicher zu modellieren. Das xmonad Design-Dokument behandelt vieles davon: xmonad.wordpress.com/2009/09/09/…
Don Stewart

3
Ich habe versucht, die Folien aus dem Vortrag " Engineering Large Projects in Haskell" herunterzuladen , aber der Link schien defekt zu sein. Hier ist eine funktionierende: galois.com/~dons/talks/dons-londonhug-decade.pdf
mik01aj

3
Ich habe es geschafft, diesen neuen Download-Link zu finden: pau-za.cz/data/2/sprava.pdf
Riccardo T.

3
@Heather Auch wenn der Download-Link auf der Seite, die ich direkt im Kommentar erwähnt habe, nicht funktioniert, können die Folien anscheinend immer noch auf scribd angezeigt werden: scribd.com/doc/19503176/The-Design-and-Implementation-of -xmonad
Riccardo T.

118

Don hat Ihnen die meisten der oben genannten Details gegeben, aber hier sind meine zwei Cent, wenn ich wirklich knifflige Stateful-Programme wie System-Daemons in Haskell mache.

  1. Am Ende leben Sie in einem Monadentransformator-Stack. Unten ist IO. Darüber hinaus ordnet jedes Hauptmodul (im abstrakten Sinne, nicht im Sinne eines Moduls in einer Datei) seinen erforderlichen Zustand einer Ebene in diesem Stapel zu. Wenn Sie also Ihren Datenbankverbindungscode in einem Modul versteckt haben, schreiben Sie alles über einen Typ MonadReader Connection m => ... -> m ... und dann können Ihre Datenbankfunktionen ihre Verbindung immer ohne Funktionen von anderen erhalten Module müssen sich ihrer Existenz bewusst sein. Möglicherweise haben Sie eine Schicht mit Ihrer Datenbankverbindung, eine andere mit Ihrer Konfiguration, eine dritte mit Ihren verschiedenen Semaphoren und MVARs für die Auflösung von Parallelität und Synchronisation, eine andere mit Ihren Protokolldateien usw.

  2. Herauszufinden , Ihre Fehlerbehandlung zuerst . Die derzeit größte Schwäche von Haskell in größeren Systemen ist die Fülle von Fehlerbehandlungsmethoden, einschließlich mieser Methoden wie "Vielleicht" (was falsch ist, weil Sie keine Informationen darüber zurückgeben können, was schief gelaufen ist. Verwenden Sie immer "Entweder" statt "Vielleicht", es sei denn, Sie tun es wirklich nur fehlende Werte bedeuten). Finden Sie heraus, wie Sie es zuerst tun werden, und richten Sie Adapter aus den verschiedenen Fehlerbehandlungsmechanismen ein, die Ihre Bibliotheken und anderer Code in Ihrem endgültigen verwenden. Dies wird Ihnen später eine Welt der Trauer ersparen.

Nachtrag (aus Kommentaren extrahiert; danke an Lii & liminalisht ) -
mehr Diskussion über verschiedene Möglichkeiten, ein großes Programm in Monaden in einem Stapel aufzuteilen:

Ben Kolera gibt eine großartige praktische Einführung in dieses Thema, und Brian Hurt diskutiert Lösungen für das Problem, liftmonadische Aktionen in Ihre benutzerdefinierte Monade zu integrieren. George Wilson zeigt, wie Sie mtlCode schreiben, der mit jeder Monade funktioniert, die die erforderlichen Typklassen implementiert, und nicht mit Ihrer benutzerdefinierten Monadenart. Carlo Hamalainen hat einige kurze, nützliche Notizen geschrieben, die Georges Vortrag zusammenfassen.


5
Zwei gute Punkte! Diese Antwort hat den Verdienst, einigermaßen konkret zu sein, was die anderen nicht sind. Es wäre interessant, mehr Diskussionen über verschiedene Möglichkeiten zu lesen, ein großes Programm in Monaden in einem Stapel aufzuteilen. Bitte posten Sie Links zu solchen Artikeln, wenn Sie welche haben!
Lii

6
@Lii Ben Kolera bietet eine großartige praktische Einführung in dieses Thema, und Brian Hurt diskutiert Lösungen für das Problem, liftmonadische Aktionen in Ihre benutzerdefinierte Monade zu integrieren. George Wilson zeigt, wie Sie mtlCode schreiben, der mit jeder Monade funktioniert, die die erforderlichen Typklassen implementiert, und nicht mit Ihrer benutzerdefinierten Monadenart. Carlo Hamalainen hat einige kurze, nützliche Notizen geschrieben, die Georges Vortrag zusammenfassen.
Liminalisht

Ich bin damit einverstanden, dass Monadentransformatorstapel in der Regel wichtige architektonische Grundlagen sind, aber ich bemühe mich sehr, IO von ihnen fernzuhalten. Es ist nicht immer möglich, aber wenn Sie darüber nachdenken, was "und dann" in Ihrer Monade bedeutet, stellen Sie möglicherweise fest, dass Sie wirklich irgendwo unten eine Fortsetzung oder einen Automaten haben, der dann durch eine "Ausführen" -Funktion in E / A interpretiert werden kann.
Paul Johnson

Wie @PaulJohnson bereits betont hat, scheint dieser Monad Transformer Stack-Ansatz im Widerspruch zu Michael Snoymans ReaderT Design Pattern
McBear Holden

43

Das Entwerfen großer Programme in Haskell unterscheidet sich nicht wesentlich von anderen Programmen. Beim Programmieren im großen Stil geht es darum, Ihr Problem in überschaubare Teile zu zerlegen und diese zusammenzufügen. Die Implementierungssprache ist weniger wichtig.

Trotzdem ist es in einem großen Design schön, das Typensystem zu nutzen, um sicherzustellen, dass Sie Ihre Teile nur auf die richtige Weise zusammenfügen können. Dies kann Newtype- oder Phantomtypen beinhalten, damit Dinge, die den gleichen Typ zu haben scheinen, unterschiedlich sind.

Wenn es darum geht, den Code im Laufe der Zeit umzugestalten, ist Reinheit ein großer Segen. Versuchen Sie also, so viel Code wie möglich rein zu halten. Reiner Code lässt sich leicht umgestalten, da er keine versteckte Interaktion mit anderen Teilen Ihres Programms aufweist.


14
Ich fand tatsächlich, dass Refactoring ziemlich frustrierend ist, wenn sich die Datentypen ändern müssen. Es erfordert eine mühsame Änderung der Arität vieler Konstruktoren und Musterübereinstimmungen. (Ich stimme zu, dass es einfach ist, reine Funktionen in andere reine Funktionen des gleichen Typs umzuwandeln - solange man die Datentypen nicht berührt)
Dan

2
@Dan Sie können mit kleineren Änderungen (wie dem Hinzufügen eines Feldes) völlig kostenlos davonkommen, wenn Sie Datensätze verwenden. Einige möchten vielleicht Aufzeichnungen zur Gewohnheit machen (ich bin einer von ihnen ^^ ").
MasterMastic

5
@Dan ich meine, wenn Sie den Datentyp einer Funktion in einer beliebigen Sprache ändern, müssen Sie nicht dasselbe tun? Ich sehe nicht ein, wie eine Sprache wie Java oder C ++ Ihnen in dieser Hinsicht helfen würde. Wenn Sie sagen, dass Sie eine Art gemeinsame Schnittstelle verwenden können, der beide Typen gehorchen, sollten Sie dies mit Typeclasses in Haskell getan haben.
Semikolon

4
@semicon Der Unterschied für Sprachen wie Java besteht in der Existenz ausgereifter, gut getesteter und vollautomatisierter Tools für das Refactoring. Im Allgemeinen verfügen diese Tools über eine fantastische Editorintegration und nehmen einen großen Teil der mühsamen Arbeit im Zusammenhang mit dem Refactoring weg. Haskell bietet uns ein brillantes Typsystem, mit dem wir Dinge erkennen können, die bei einem Refactoring geändert werden müssen, aber die Tools, um dieses Refactoring tatsächlich durchzuführen, sind (derzeit) sehr begrenzt, insbesondere im Vergleich zu dem, was bereits in Java verfügbar war Ökosystem seit mehr als 10 Jahren.
jsk

16

Mit diesem Buch habe ich zum ersten Mal strukturierte funktionale Programmierung gelernt . Es ist vielleicht nicht genau das, wonach Sie suchen, aber für Anfänger in der funktionalen Programmierung ist dies möglicherweise einer der besten ersten Schritte, um zu lernen, funktionale Programme zu strukturieren - unabhängig von der Skala. Auf allen Abstraktionsebenen sollte das Design immer klar angeordnete Strukturen haben.

Das Handwerk der funktionalen Programmierung

Das Handwerk der funktionalen Programmierung

http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/


11
So großartig das Craft of FP auch ist - ich habe Haskell daraus gelernt - es ist ein Einführungstext für Anfängerprogrammierer , nicht für den Entwurf großer Systeme in Haskell.
Don Stewart

3
Nun, es ist das beste Buch, das ich über das Entwerfen von APIs und das Ausblenden von Implementierungsdetails kenne. Mit diesem Buch wurde ich ein besserer Programmierer in C ++ - nur weil ich bessere Möglichkeiten zum Organisieren meines Codes gelernt habe. Nun, Ihre Erfahrung (und Antwort) ist sicherlich besser als dieses Buch, aber Dan könnte wahrscheinlich noch ein Anfänger in Haskell sein. ( where beginner=do write $ tutorials `about` Monads)
Comonad

11

Ich schreibe gerade ein Buch mit dem Titel "Functional Design and Architecture". Es bietet Ihnen einen vollständigen Satz von Techniken zum Erstellen einer großen Anwendung unter Verwendung eines rein funktionalen Ansatzes. Es beschreibt viele funktionale Muster und Ideen beim Aufbau einer SCADA-ähnlichen Anwendung 'Andromeda' zur Steuerung von Raumschiffen von Grund auf neu. Meine Hauptsprache ist Haskell. Das Buch behandelt:

  • Ansätze zur Architekturmodellierung anhand von Diagrammen;
  • Anforderungsanalyse;
  • Modellierung eingebetteter DSL-Domänen;
  • Externes DSL-Design und Implementierung;
  • Monaden als Subsysteme mit Effekten;
  • Freie Monaden als funktionale Schnittstellen;
  • Pfeilförmige eDSLs;
  • Inversion der Kontrolle mit freien monadischen eDSLs;
  • Software-Transaktionsspeicher;
  • Linsen;
  • Staat, Leser, Schriftsteller, RWS, ST-Monaden;
  • Unreiner Zustand: IORef, MVar, STM;
  • Multithreading und gleichzeitige Domänenmodellierung;
  • GUI;
  • Anwendbarkeit von Mainstream-Techniken und -Ansätzen wie UML, SOLID, GRASP;
  • Interaktion mit unreinen Subsystemen.

Sie können mit dem Code für das Buch vertraut hier , und der ‚Andromeda‘ Projektcode.

Ich erwarte , dass dieses Buch am Ende 2017. beenden Bis das geschieht, können Sie meinen Artikel „Design and Architecture in Functional Programming“ (Rus) lesen Sie hier .

AKTUALISIEREN

Ich habe mein Buch online geteilt (erste 5 Kapitel). Siehe Beitrag auf Reddit


Alexander, kannst du diese Notiz bitte aktualisieren, wenn dein Buch fertig ist, damit wir ihr folgen können? Prost.
Max

4
Sicher! Im Moment habe ich die Hälfte des Textes fertiggestellt, aber es ist ein Drittel der Gesamtarbeit. Also, behalten Sie Ihr Interesse, das inspiriert mich sehr!
Graninas

2
Hallo! Ich habe mein Buch online geteilt (nur die ersten 5 Kapitel). Siehe Beitrag auf Reddit: reddit.com/r/haskell/comments/6ck72h/…
Graninas

Danke fürs Teilen und Arbeiten!
Max

Freue mich wirklich darauf!
Patriques

7

Gabriels Blog-Beitrag Skalierbare Programmarchitekturen könnten eine Erwähnung wert sein.

Haskell-Designmuster unterscheiden sich in einem wichtigen Punkt von den gängigen Designmustern:

  • Konventionelle Architektur : Kombinieren Sie mehrere Komponenten vom Typ A, um ein "Netzwerk" oder eine "Topologie" vom Typ B zu generieren

  • Haskell-Architektur : Kombinieren Sie mehrere Komponenten des Typs A, um eine neue Komponente des gleichen Typs A zu erzeugen, deren Charakter nicht von ihren Substituenten zu unterscheiden ist

Es fällt mir oft auf, dass eine scheinbar elegante Architektur oft aus Bibliotheken herausfällt, die dieses schöne Gefühl der Homogenität von unten nach oben aufweisen. In Haskell ist dies besonders deutlich - Muster, die traditionell als "Top-Down-Architektur" bezeichnet werden, werden in der Regel in Bibliotheken wie mvc , Netwire und Cloud Haskell erfasst . Das heißt, ich hoffe, diese Antwort wird nicht als Versuch interpretiert, einen der anderen in diesem Thread zu ersetzen, sondern nur, dass strukturelle Entscheidungen von Domain-Experten in Bibliotheken idealerweise abstrahiert werden können und sollten. Die eigentliche Schwierigkeit beim Aufbau großer Systeme besteht meiner Meinung nach darin, diese Bibliotheken hinsichtlich ihrer architektonischen "Güte" im Vergleich zu all Ihren pragmatischen Anliegen zu bewerten.

Wie liminalisht in den Kommentaren erwähnt, In der Kategorie Design - Muster ist ein weiterer Beitrag von Gabriel zum Thema, in ähnlicher Weise.


3
Ich würde einen weiteren Beitrag von Gabriel Gonzalez zum Designmuster der Kategorie erwähnen . Sein grundlegendes Argument ist, dass das, was wir funktionalen Programmierer als "gute Architektur" betrachten, wirklich "kompositorische Architektur" ist - es entwirft Programme unter Verwendung von Elementen, die garantiert komponieren. Da die Kategorie Gesetze , die Identität und die Assoziativität garantieren unter Zusammensetzung erhalten werden, wird eine kompositorische Architektur durch Abstraktionen , für die wir eine Kategorie haben mit erreicht - zB reinen Funktionen, monadischen Aktionen, Rohre usw.
liminalisht


3

Vielleicht müssen Sie einen Schritt zurücktreten und überlegen, wie Sie die Beschreibung des Problems überhaupt in ein Design übersetzen können. Da Haskell auf so hohem Niveau ist, kann es die Beschreibung des Problems in Form von Datenstrukturen, die Aktionen als Prozeduren und die reine Transformation als Funktionen erfassen. Dann haben Sie ein Design. Die Entwicklung beginnt, wenn Sie diesen Code kompilieren und konkrete Fehler in Bezug auf fehlende Felder, fehlende Instanzen und fehlende monadische Transformatoren in Ihrem Code finden, da Sie beispielsweise einen Datenbankzugriff von einer Bibliothek aus durchführen, die eine bestimmte Statusmonade innerhalb einer E / A-Prozedur benötigt. Und voila, da ist das Programm. Der Compiler füttert Ihre mentalen Skizzen und gibt dem Design und der Entwicklung Kohärenz.

Auf diese Weise profitieren Sie von Anfang an von der Hilfe von Haskell, und die Codierung ist natürlich. Ich würde nicht gerne etwas "Funktionales" oder "Reines" oder genug Allgemeines tun, wenn das, was Sie im Sinn haben, ein konkretes gewöhnliches Problem ist. Ich denke, dass Over-Engineering das Gefährlichste in der IT ist. Anders sieht es aus, wenn das Problem darin besteht, eine Bibliothek zu erstellen, die eine Reihe verwandter Probleme abstrahiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.