Nach dem Erlernen der funktionalen Programmierung in Haskell und F # scheint das OOP-Paradigma mit Klassen, Schnittstellen und Objekten rückständig zu sein. Welche Aspekte von FP kann ich zur Arbeit bringen, die meine Mitarbeiter verstehen können? Lohnt es sich, mit meinem Chef über die Umschulung meines Teams zu sprechen, damit wir sie einsetzen können?
Mögliche Aspekte von FP:
- Unveränderlichkeit
- Teilanwendung und Currying
- First-Class-Funktionen (Funktionszeiger / Funktionsobjekte / Strategiemuster)
- Lazy Evaluation (und Monaden)
- Reine Funktionen (keine Nebenwirkungen)
- Ausdrücke (vs. Anweisungen - jede Codezeile erzeugt einen Wert anstatt oder zusätzlich zu Nebenwirkungen)
- Rekursion
- Mustervergleich
Ist es ein Alleskönner, bei dem wir alles tun können, was die Programmiersprache unterstützt, bis zu der Grenze, in der die Sprache es unterstützt? Oder gibt es eine bessere Richtlinie?
Antworten:
Funktionale Programmierung ist ein anderes Paradigma als objektorientierte Programmierung (eine andere Denkweise und eine andere Art, über Programme zu denken). Sie haben erkannt, dass es hier mehr als eine Möglichkeit (objektorientiert) gibt, über Probleme und deren Lösungen nachzudenken. Es gibt andere (prozedurale und generische Programmierung kommen in den Sinn). Wie Sie auf dieses neue Wissen reagieren, ob Sie diese neuen Tools und Ansätze akzeptieren und in Ihre Fähigkeiten integrieren, entscheidet darüber, ob Sie wachsen und ein vollständigerer, erfahrener Entwickler werden.
Wir sind alle geschult und beherrschen eine gewisse Komplexität. Ich nenne dies gerne das Hrair- Limit einer Person (von Watership Down, wie hoch kann man zählen). Es ist eine großartige Sache, Ihren Verstand zu erweitern, mehr Optionen in Betracht zu ziehen und mehr Werkzeuge zu haben, um Probleme anzugehen und zu lösen. Aber es ist eine Veränderung, die Sie aus Ihrer Komfortzone zieht.
Ein Problem, auf das Sie stoßen können, ist, dass Sie weniger zufrieden sind, der Menge "Alles ist ein Objekt" zu folgen. Möglicherweise müssen Sie Geduld entwickeln, wenn Sie mit Menschen arbeiten, die möglicherweise nicht verstehen (oder verstehen möchten), warum ein funktionaler Ansatz für die Softwareentwicklung bei bestimmten Problemen gut funktioniert. Genauso gut funktioniert ein generischer Programmieransatz bei bestimmten Problemen.
Viel Glück!
quelle
Die funktionale Programmierung führt zu einer sehr praktischen, bodenständigen Produktivität beim alltäglichen Schreiben von Code: Einige Funktionen begünstigen die Kürze. Dies ist großartig, da je weniger Code Sie schreiben, desto weniger Fehler auftreten und desto weniger Wartung erforderlich ist.
Als Mathematiker finde ich ausgefallene Funktionen sehr ansprechend, aber sie sind normalerweise hilfreich beim Entwerfen einer Anwendung: Diese Strukturen können viele Invarianten des Programms in der Programmstruktur codieren, ohne diese Invarianten durch Variablen darzustellen.
Meine Lieblingskombination mag ziemlich trivial aussehen, ich glaube jedoch, dass sie einen sehr hohen Einfluss auf die Produktivität hat. Diese Kombination ist Teilanwendung und Currying und First-Class-Funktionen, die ich neu benennen würde, um nie wieder eine for-Schleife zu schreiben . Ich wurde vor kurzem für einen C ++ - Job eingestellt und merkwürdigerweise habe ich die Gewohnheit verloren, for-loops zu schreiben !
Die Kombination aus Rekursion und Mustererkennung macht die Notwendigkeit dieses Besucherdesignmusters zunichte . Vergleichen Sie einfach den Code, den Sie benötigen, um einen Evaluator für boolesche Ausdrücke zu programmieren: In jeder funktionalen Programmiersprache sollten dies etwa 15 Codezeilen sein. In OOP ist es das Richtige, dieses Besucher- Designmuster zu verwenden, das dieses Spielzeugbeispiel zum Vorbild macht ein umfangreicher Aufsatz. Die Vorteile liegen auf der Hand und mir sind keine Unannehmlichkeiten bekannt.
quelle
list.forEach(System.out::println);
Aus FP-Sichtprintln
besteht eine Funktion aus zwei Argumenten, einem ZielPrintStream
und einem Wert,Object
aber die MethodeCollection
'sforEach
erwartet eine Funktion mit nur einem Argument die auf jedes Element angewendet werden kann. Das erste Argument ist also an die Instanz gebunden, die darin bestehtSystem.out
, eine neue Funktion mit einem Argument zu ergeben. Es ist einfacher alsBiConsumer<…> c=PrintStream::println; PrintStream a1=System.out; list.forEach(a2 -> c.accept(a1, a2));
Sie müssen möglicherweise einschränken, welche Teile Ihres Wissens Sie bei der Arbeit verwenden, wie Superman vorgeben muss, Clark Kent zu sein, um die Vorteile eines normalen Lebens zu genießen. Aber mehr zu wissen, wird dir niemals weh tun. Allerdings sind einige Aspekte der funktionalen Programmierung für einen objektorientierten Shop geeignet, und andere Aspekte können es wert sein, mit Ihrem Chef darüber zu sprechen, damit Sie den durchschnittlichen Wissensstand Ihres Shops erhöhen und dadurch besseren Code schreiben können.
FP und OOP schließen sich nicht aus. Schau dir Scala an. Einige halten es für das Schlimmste, weil es ein unreines FP ist, andere halten es aus demselben Grund für das Beste.
Hier sind einige Aspekte, die sich hervorragend für OOP eignen:
Reine Funktionen (keine Nebenwirkungen) - Jede mir bekannte Programmiersprache unterstützt dies. Sie machen Ihren Code viel einfacher zu überlegen und sollten verwendet werden, wann immer dies praktisch ist. Sie müssen es nicht FP nennen. Nennen Sie es einfach gute Codierungspraktiken.
Unveränderlichkeit: String ist wohl das am häufigsten verwendete Java-Objekt und unveränderlich. In meinem Blog beschreibe ich unveränderliche Java-Objekte und unveränderliche Java-Sammlungen . Einige davon können auf Sie zutreffen.
First-Class-Funktionen (Funktionszeiger / Funktionsobjekte / Strategiemuster) - Java hat seit Version 1.1 eine manipulierte, mutierte Version davon, wobei die meisten API-Klassen (und es gibt Hunderte) die Listener-Schnittstelle implementieren. Runnable ist wahrscheinlich das am häufigsten verwendete Funktionsobjekt. Erstklassige Funktionen sind mehr Arbeit, um in einer Sprache zu programmieren, die sie nicht von Haus aus unterstützt, aber manchmal den zusätzlichen Aufwand wert, wenn sie andere Aspekte Ihres Codes vereinfachen.
Rekursion ist nützlich für die Verarbeitung von Bäumen. In einem OOP-Shop ist dies wahrscheinlich die primäre angemessene Verwendung der Rekursion. Die Verwendung von Rekursion zum Spaß in OOP sollte wahrscheinlich verpönt werden, wenn aus keinem anderen Grund als den meisten OOP-Sprachen standardmäßig kein Stapelspeicher zur Verfügung steht, um dies zu einer guten Idee zu machen.
Ausdrücke (im Gegensatz zu Anweisungen - jede Codezeile erzeugt einen Wert anstelle von oder zusätzlich zu den Nebenwirkungen) - Der einzige auswertende Operator in C, C ++ und Java ist der Ternäre Operator . Auf meinem Blog diskutiere ich über die angemessene Verwendung. Möglicherweise schreiben Sie einige einfache Funktionen, die in hohem Maße wiederverwendbar und auswertbar sind.
Lazy Evaluation (und Monaden) - hauptsächlich auf Lazy Initialization in OOP beschränkt. Ohne Sprachfunktionen, die dies unterstützen, finden Sie möglicherweise einige nützliche APIs, aber das Schreiben eigener APIs ist schwierig. Maximieren Sie stattdessen die Verwendung von Streams - Beispiele finden Sie in den Benutzeroberflächen "Writer" und "Reader".
Teilanwendung und Currying - Ohne erstklassige Funktionen nicht praktikabel.
Pattern Matching - in der Regel in OOP entmutigt.
Zusammenfassend sollte die Arbeit meines Erachtens kein alltägliches Unterfangen sein, bei dem Sie alles tun können, was die Programmiersprache bis zu der von der Sprache unterstützten Grenze unterstützt. Ich denke, die Lesbarkeit durch Ihre Mitarbeiter sollte Ihr Lackmustest für gemieteten Code sein. Wo das dich am meisten ärgert, würde ich versuchen, eine Ausbildung bei der Arbeit zu beginnen, um den Horizont deiner Kollegen zu erweitern.
quelle
public interface BiConsumer<T, U> { public void accept(T t, U u); }
nützlich sein kann: In java.util.function gibt es weitere nützliche Funktionsschnittstellen.Most OOP languages don't have the stack space for it
"Ja wirklich?" Sie benötigen lediglich 30 Rekursionsebenen, um Milliarden von Knoten in einem ausgeglichenen Binärbaum zu verwalten. Ich bin mir ziemlich sicher, dass mein Stapelplatz für viel mehr Ebenen als diese geeignet ist.Neben funktionaler Programmierung und objektorientierter Programmierung gibt es auch deklarative Programmierung (SQL, XQuery). Das Erlernen jedes Stils hilft Ihnen dabei, neue Erkenntnisse zu gewinnen, und Sie lernen, das richtige Werkzeug für den Job auszuwählen.
Aber ja, es kann sehr frustrierend sein, Code in einer Sprache zu schreiben und zu wissen, dass Sie, wenn Sie etwas anderes verwenden, für eine bestimmte Problemdomäne viel produktiver sein könnten. Selbst wenn Sie eine Sprache wie Java verwenden, ist es möglich, Konzepte von FP auf Ihren Java-Code anzuwenden, wenn auch auf Umwegen. Das Guava-Framework macht zum Beispiel einiges davon.
quelle
Als Programmierer sollte man nie aufhören zu lernen. Das heißt, es ist sehr interessant, dass das Lernen von FP Ihre OOP-Fähigkeiten beeinträchtigt. Ich neige dazu, OOP zu lernen, als zu lernen, wie man Fahrrad fährt. du vergisst nie, wie es geht.
Als ich die Vor- und Nachteile von FP lernte, musste ich mathematischer denken und mir einen besseren Überblick über die Mittel verschaffen, mit denen ich Software schreibe. Das ist meine persönliche Erfahrung.
Je mehr Erfahrung Sie sammeln, desto schwerer wird es, grundlegende Programmierkonzepte zu verlieren. Deshalb schlage ich vor, dass Sie das FP so lange locker angehen, bis die OOP-Konzepte in Ihrem Kopf vollständig gefestigt sind. FP ist ein eindeutiger Paradigmenwechsel. Viel Glück!
quelle
Es gibt bereits viele gute Antworten, daher werden meine Antworten einen Teil Ihrer Frage beantworten. Ich nehme die Prämisse Ihrer Frage in den Hintergrund, da sich OOP und Funktionsmerkmale nicht gegenseitig ausschließen.
Wenn Sie C ++ 11 verwenden, enthält die Sprach- / Standardbibliothek viele dieser funktionalen Programmierfunktionen, die sich gut mit OOP kombinieren lassen. Natürlich bin ich mir nicht sicher, wie gut TMP von Ihrem Chef oder Ihren Mitarbeitern empfangen wird, aber der Punkt ist, dass Sie viele dieser Funktionen in irgendeiner Form in nicht funktionalen / OOP-Sprachen wie C ++ erhalten können.
Die Verwendung von Vorlagen mit Rekursion zur Kompilierungszeit basiert auf Ihren ersten 3 Punkten.
Da Template-Werte unveränderlich sind (Konstanten für die Kompilierungszeit), wird jede Iteration unter Verwendung von Rekursion ausgeführt, und die Verzweigung wird unter Verwendung von (mehr oder weniger) Musterabgleich in Form einer Überladungsauflösung ausgeführt.
Was die anderen Punkte betrifft, verwenden Sie
std::bind
und, umstd::function
eine teilweise Funktionsanwendung zu erhalten, und Funktionszeiger sind in die Sprache integriert. Aufrufbare Objekte sind Funktionsobjekte (sowie Teilfunktionsanwendungen). Beachten Sie, dass mit aufrufbaren Objekten diejenigen gemeint sind, die ihre definierenoperator ()
.Lazy Evaluation und reine Funktionen wären etwas schwieriger; Für reine Funktionen können Sie Lambda-Funktionen verwenden, die nur nach Wert erfassen. Dies ist jedoch nicht ideal.
Im Folgenden finden Sie ein Beispiel für die Verwendung der Rekursion zur Kompilierungszeit mit Teilfunktionsanwendung. Es ist ein etwas ausgeklügeltes Beispiel, aber es zeigt die meisten der obigen Punkte. Es bindet die Werte in einem gegebenen Tupel rekursiv an eine gegebene Funktion und erzeugt ein (aufrufbares) Funktionsobjekt
quelle