Frage

Software-Engineering, wie es heute gelehrt wird, konzentriert sich vollständig auf objektorientierte Programmierung und die 'natürliche' objektorientierte Sichtweise der Welt. Es gibt eine detaillierte Methodik, in der beschrieben wird, wie ein Domänenmodell in ein Klassenmodell mit mehreren Schritten und vielen (UML-) Artefakten wie Anwendungs-Diagrammen oder Klassendiagramme umgewandelt werden kann. Viele Programmierer haben diesen Ansatz verinnerlicht und haben eine gute Vorstellung davon, wie eine objektorientierte Anwendung von Grund auf neu gestaltet werden kann.

Der neue Hype ist eine funktionale Programmierung, die in vielen Büchern und Tutorials unterrichtet wird. Aber was ist mit funktionaler Softwaretechnik? Während ich über Lisp und Clojure las, kam ich zu zwei interessanten Aussagen:

  1. Funktionelle Programme werden häufig unten nach oben anstelle von oben nach unten entwickelt ('On Lisp', Paul Graham)

  2. Funktionelle Programmierer verwenden Karten, bei denen OO-Programmierer Objekte/Klassen verwenden ('Clojure for Java-Programmierern', Talk von Rich Hickley).

Wie ist die Methodik für ein systematisches (modellbasiertes?) Design einer funktionalen Anwendung, dh in Lisp oder Clojure? Was sind die allgemeinen Schritte, welche Artefakte verwenden ich, wie kann ich sie vom Problemraum zum Lösungsraum zuordnen?

War es hilfreich?

Lösung

Gott sei Dank, dass die Software-Engineering-Menschen noch keine funktionale Programmierung entdeckt haben. Hier sind einige Parallelen:

  • Viele OO-Designmuster werden als Funktionen höherer Ordnung erfasst. Zum Beispiel ist das Besuchermuster in der funktionalen Welt als "Falten" bekannt (oder wenn Sie ein spitzköpfiger Theoretiker sind, ein "Katamorphismus"). In funktionellen Sprachen sind Datentypen hauptsächlich Bäume oder Tupel, und jeder Baumtyp hat einen natürlichen Katamorphismus, der damit verbunden ist.

    Diese Funktionen höherer Ordnung sind oft mit bestimmten Programmen der Programmierung ausgestattet, auch bekannt als "freie Theoreme".

  • Funktionelle Programmierer verwenden Diagramme viel weniger stark als OO -Programmierer. Vieles, was in OO -Diagrammen ausgedrückt wird Typen, oder in "Signaturen", die Sie als "Modultypen" betrachten sollten. Haskell hat auch "Typklassen", was ein bisschen wie ein Schnittstellentyp ähnelt.

    Diejenigen funktionalen Programmierer, die Typen verwenden, denken im Allgemeinen, dass "der Code praktisch selbst schreibt", wenn Sie die Typen richtig machen. "

    Nicht alle funktionalen Sprachen verwenden explizite Typen, sondern die So entwerfen Sie Programme Buch, ein ausgezeichnetes Buch für Lernschema/Lisp/Clojure, beruht stark auf "Datenbeschreibungen", die eng mit Typen zusammenhängen.

Wie ist die Methodik für ein systematisches (modellbasiertes?) Design einer funktionalen Anwendung, dh in Lisp oder Clojure?

Jede auf Datenabstraktion basierende Entwurfsmethode funktioniert gut. Ich denke zufällig, dass dies einfacher ist, wenn die Sprache explizite Typen hat, aber es funktioniert auch ohne. Ein gutes Buch über Designmethoden für abstrakte Datentypen, die leicht an die funktionale Programmierung angepasst werden können, ist Abstraktion und Spezifikation in der Programmentwicklung von Barbara Liskov und John Guttag, die Erste Auflage. Liskov gewann den Turing Award zum Teil für diese Arbeit.

Eine weitere Designmethodik, die für LISP einzigartig ist, besteht darin, zu entscheiden, welche Sprachverlängerungen in der Problemdomäne, in der Sie arbeiten, nützlich wäre, und dann hygienische Makros verwenden, um diese Konstrukte zu Ihrer Sprache hinzuzufügen. Ein guter Ort, um über diese Art von Design zu lesen, ist Matthew Flatts Artikel Sprachen im Schläger erstellen. Der Artikel kann hinter einer Paywall stehen. Sie können auch allgemeineres Material für diese Art von Design finden, indem Sie nach dem Begriff "domänenspezifische eingebettete Sprache" suchen. Für bestimmte Ratschläge und Beispiele, die über das hinausgeht, was Matthew Flatt abdeckt, würde ich wahrscheinlich mit Graham's beginnen Auf Lisp oder vielleicht ANSI Common Lisp.

Was sind die allgemeinen Schritte, welche Artefakte verwenden ich?

Häufige Schritte:

  1. Identifizieren Sie die Daten in Ihrem Programm und die Operationen darauf und definieren Sie einen abstrakten Datentyp, der diese Daten darstellt.

  2. Identifizieren Sie gemeinsame Aktionen oder Berechnungsmuster und drücken Sie sie als Funktionen höherer Ordnung oder Makros aus. Erwarten Sie diesen Schritt im Rahmen des Refactoring.

  3. Wenn Sie eine typisierte funktionale Sprache verwenden, verwenden Sie den Typ Checker früh und häufig. Wenn Sie Lisp oder Clojure verwenden, besteht die beste Praxis darin, zuerst Funktionsverträge zu schreiben, einschließlich Unit-Tests-es von der Test-gesteuerte Entwicklung des Maximum. Und Sie möchten die Version von QuickCheck verwenden, die auf Ihre Plattform portiert wurde, was in Ihrem Fall so aussieht, als würde sie genannt Clojurecheck. Es handelt sich um eine äußerst leistungsstarke Bibliothek, um zufällige Code-Tests zu erstellen, die Funktionen höherer Ordnung verwendet.

Andere Tipps

Für Clojure empfehle ich, wieder auf eine gute alte relationale Modellierung zurückzukehren. Aus der Plane ist eine inspirierende Lektüre.

Persönlich finde ich, dass alle üblichen guten Praktiken aus der OO -Entwicklung auch in funktionaler Programmierung gelten - nur mit ein paar kleinen Änderungen, um die funktionale Weltanschauung zu berücksichtigen. Aus methodologischer Sicht müssen Sie nichts grundlegend anderes tun.

Meine Erfahrung besteht darin, in den letzten Jahren von Java zu Clojure gewechselt zu sein.

Einige Beispiele:

  • Verstehen Sie Ihr Geschäftsdomänen- / Datenmodell - Ebenso wichtig, ob Sie ein Objektmodell entwerfen oder eine funktionale Datenstruktur mit verschachtelten Karten erstellen. In gewisser Weise kann FP einfacher sein, da es Sie dazu ermutigt, über das Datenmodell getrennt von Funktionen / Prozessen nachzudenken, aber Sie müssen trotzdem beides tun.

  • Serviceorientierung im Design - Wir funktionieren aus einer FP -Sicht sehr gut, da ein typischer Dienst wirklich nur eine Funktion mit einigen Nebenwirkungen ist. Ich denke, dass die "Bottom-up" -Sicht auf die Softwareentwicklung, die manchmal in der Lisp-Welt eintritt, nur gute dienstwertige API-Designprinzipien in einem anderen Gestalt ist.

  • Testgetriebene Entwicklung - Funktioniert gut in FP -Sprachen, tatsächlich sogar noch besser, da sich reine Funktionen sehr gut für klare, wiederholbare Tests eignen, ohne dass eine staatliche Umgebung eingerichtet werden muss. Möglicherweise möchten Sie auch separate Tests erstellen, um die Datenintegrität zu überprüfen (z. B. diese Karte verfügt über alle Schlüssel, die ich erwarte, um die Tatsache auszugleichen, dass die Klassendefinition dies für Sie zum Kompilierenzeitpunkt durchsetzen würde).

  • Prototying / Iteration - funktioniert genauso gut mit FP. Möglicherweise können Sie sogar live mit Benutzern prototypisieren, wenn Sie extrem gut darin sind, Tools / DSL zu bauen und sie bei der Repl.

OO programmieren eng Daten mit Verhalten. Die funktionelle Programmierung trennt die beiden. Sie haben also keine Klassendiagramme, aber Datenstrukturen und besonders algebraische Datentypen. Diese Typen können so geschrieben werden, dass sie sehr eng mit Ihrer Domain übereinstimmen, einschließlich der Beseitigung unmöglicher Werte durch Konstruktion.

Es gibt also keine Bücher und Bücher darüber, aber es gibt eine gut etablierte Herangehensweise, wie das Sprichwort unmögliche Werte nicht darstellbar macht.

Auf diese Weise können Sie eine Reihe von Auswahlmöglichkeiten zur Darstellung bestimmter Datenarten als Funktionen treffen und umgekehrt bestimmte Funktionen als Vereinigung von Datentypen darstellen, damit Sie, z. B. Serialisierung, engere Spezifikation, Optimierung usw., erhalten können, usw. .

Angesichts der Tatsache schreiben Sie Funktionen über Ihre ADTs, so dass Sie eine Art von einer Art festlegen Algebra - dh es gibt feste Gesetze, die für diese Funktionen enthalten sind. Einige sind möglicherweise idempotent - dasselbe nach mehreren Anwendungen. Einige sind assoziativ. Einige sind transitiv usw.

Jetzt haben Sie eine Domain, über die Sie Funktionen haben, die nach gut verhaltenen Gesetzen zusammenhängen. Ein einfaches eingebettetes DSL!

Oh, und gegebenen Eigenschaften können Sie natürlich automatisierte randomisierte Tests von ihnen schreiben (ala QuickCheck). Und das ist erst der Anfang.

Objektorientiertes Design ist nicht dasselbe wie Software Engineering. Software Engineering hat mit dem gesamten Prozess zu tun, wie wir von den Anforderungen zu einem Arbeitssystem pünktlich und mit einer geringen Defektrate wechseln. Die funktionale Programmierung kann sich von OO unterscheiden, aber es geht jedoch nicht um Anforderungen, hohe und detaillierte Designs, Überprüfung und Tests, Softwaremetriken, Schätzungen und all das andere "Software -Engineering".

Darüber hinaus zeigen Funktionsprogramme Modularität und andere Struktur. Ihre detaillierten Entwürfe müssen in Bezug auf die Konzepte in dieser Struktur ausgedrückt werden.

Ein Ansatz besteht darin, eine interne DSL innerhalb der funktionalen Programmiersprache der Wahl zu erstellen. Das "Modell" ist dann eine Reihe von Geschäftsregeln, die in der DSL ausgedrückt werden.

Siehe meine Antwort auf einen anderen Beitrag:

Wie macht die Clojure -Aphe -Trennung von Bedenken?

Ich bin damit einverstanden, dass mehr über das Thema geschrieben werden muss, wie große Anwendungen strukturiert werden können, die einen FP-Ansatz verwenden (und mehr muss getan werden, um die FP-gesteuerte Benutzeroberfläche zu dokumentieren).

Obwohl dies als naiv und simpel angesehen werden könnte, denke ich, dass "Designrezepte" (ein systematischer Ansatz zur Problemlösung für die Programmierung angewendet werden, wie von Fellisen et al. In ihrem Buch befürwortet Htdp) wäre nahe an das, wonach Sie zu suchen scheinen.

Hier ein paar Links:

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

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

Ich habe festgestellt, dass eine verhaltensgetriebene Entwicklung eine natürliche Passform für den sich schnell entwickelnden Code sowohl in Clojure als auch in SBCL entspricht. Der eigentliche Aufwärtstrend der Nutzung von BDD mit einer funktionalen Sprache ist, dass ich dazu neige, viel feinere Getreide -Unit -Tests zu schreiben, als ich es normalerweise bei der Verwendung von prozeduralen Sprachen mache, da ich das Problem viel besser in kleinere Funktionen der Funktionalität zerlegt habe.

Ich habe kürzlich dieses Buch gefunden:Funktionelle und reaktive Domänenmodellierung

Ich denke, passt perfekt zu Ihrer Frage.

Aus der Buchbeschreibung:

Die funktionelle und reaktive Domänenmodellierung lehrt Sie, wie Sie an das Domänenmodell in Bezug auf reine Funktionen und wie Sie sie zum Aufbau größerer Abstraktionen erstellen können. Sie beginnen mit den Grundlagen der funktionalen Programmierung und gehen schrittweise zu den erweiterten Konzepten und Mustern, die Sie wissen müssen, um komplexe Domänenmodelle zu implementieren. Das Buch zeigt, wie fortschrittliche FP-Muster wie algebraische Datentypen, typeclass-basiertes Design und die Isolierung von Nebenwirkungen Ihr Modell für Lesbarkeit und Überprüfbarkeit komponieren können.

Ehrlich gesagt, wenn Sie Designrezepte für Funktionsprogramme wünschen, schauen Sie sich die Standardfunktionsbibliotheken wie Haskells Vorspiel an. In FP werden Muster normalerweise durch Verfahren höherer Ordnung (Funktionen, die auf Funktionen wirken) selbst erfasst. Wenn also ein Muster gesehen wird, wird oft eine Funktion höherer Ordnung erstellt, um dieses Muster zu erfassen.

Ein gutes Beispiel ist FMAP. Diese Funktion nimmt eine Funktion als Argument an und wendet sie auf alle "Elemente" des zweiten Arguments an. Da es sich um Teil der Funktionstyp -Klasse handelt, kann jede Instanz eines Funktors (wie eine Liste, Diagramm usw.) als zweites Argument für diese Funktion übergeben werden. Es erfasst das allgemeine Verhalten der Anwendung einer Funktion auf jedes Element seines zweiten Arguments.

Es gibt den Stil "Programmberechnung" / "Design nach Berechnung", der mit Prof. Richard Bird und der Algebra der Programmiergruppe an der Oxford University (UK) verbunden ist. Ich glaube nicht, dass es zu weit hergeholt ist, um diese Methodik zu betrachten.

Persönlich, während ich die von der AOP -Gruppe produzierte Arbeit mag, habe ich nicht die Disziplin, das Design auf diese Weise selbst zu üben. Dies ist jedoch mein Mangel und nicht zu einer Programmberechnung.

Brunnen,

Im Allgemeinen werden viele funktionale Programmiersprachen für "kleine Spielzeugprobleme" lange Zeit an Universitäten verwendet.

Sie werden jetzt populärer, da OOP Schwierigkeiten mit "Paralel -Programmierung" aufgrund von "Zustand" hat. Und manchmal ist der funktionale Stil besser für das Problem wie Google MapReduce.

Ich bin mir sicher, dass einige von ihnen mit neuen Software-Engineering-Methoden mit Buzz-Wörtern geliefert werden :-). Sie sollten die alte Frage beantworten: Wie können Sie das System in Stücke teilen, damit wir jeweils ein Stück "beißen" können? [ITerative, unentgeltliche Weise iterativ, mit dem funktionalen Stil.

Es ist sicher, dass der funktionale Stil unseren objektorientierten Stil beeinflusst. "Wir" immer noch "viele Konzepte aus funktionalen Systemen und an unsere OOP -Sprachen angepasst.

Aber werden Funktionsprogramme für so große Systeme verwendet? Werden sie zum Hauptstrom? Das ist hier die Frage.

Und niemand kann mit realistischer Methodik kommen, ohne ein so großes System zu implementieren, was seine Hände schmutzig macht. Zuerst sollten Sie Ihre Hände schmutzig machen und dann eine Lösung vorschlagen. Lösungen ohne "echte Schmerzen und Schmutz" werden "Fantasie" sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top