Reine funktionale vs sagen, nicht fragen?

14

"Die ideale Anzahl von Argumenten für eine Funktion ist Null" ist einfach falsch. Die ideale Anzahl von Argumenten entspricht genau der Anzahl, die erforderlich ist, damit Ihre Funktion nebenwirkungsfrei ist. Weniger als das und Sie verursachen unnötigerweise, dass Ihre Funktionen unrein sind, was Sie zwingt, sich von der Grube des Erfolgs zu entfernen und den Gradienten des Schmerzes zu überwinden. Manchmal ist "Onkel Bob" mit seinem Rat genau richtig. Manchmal irrt er sich spektakulär. Sein Nullargument-Rat ist ein Beispiel für Letzteres

( Quelle: Kommentar von @David Arno unter einer anderen Frage auf dieser Seite )

Der Kommentar hat eine spektakuläre Anzahl von 133 positiven Stimmen erhalten, weshalb ich etwas mehr auf seine Verdienste achten möchte.

Soweit mir bekannt ist, gibt es zwei verschiedene Möglichkeiten bei der Programmierung: reine Funktionsprogrammierung (was dieser Kommentar anregt) und erzählen, nicht fragen (was von Zeit zu Zeit auch auf dieser Website empfohlen wird). AFAIK Diese beiden Prinzipien sind grundsätzlich inkompatibel und stehen in der Nähe von Gegensätzen: Rein funktional kann als "nur Rückgabewerte, keine Nebenwirkungen" zusammengefasst werden, während tell, nicht ask als "nichts zurückgeben" zusammengefasst werden kann. nur Nebenwirkungen haben ". Außerdem bin ich etwas ratlos, weil ich dachte, dass "Tell, Don't Ask" als Kern des OO-Paradigmas angesehen wurde, während reine Funktionen als Kern des Funktionsparadigmas angesehen wurden - jetzt sehe ich reine Funktionen, die in OO empfohlen werden!

Ich nehme an, Entwickler sollten sich wahrscheinlich für eines dieser Paradigmen entscheiden und dabei bleiben. Nun, ich muss zugeben, dass ich mich auch niemals dazu bringen könnte, zu folgen. Oft erscheint es mir zweckmäßig, einen Wert zurückzugeben, und ich kann nicht wirklich erkennen, wie ich das erreichen kann, was ich nur mit Nebenwirkungen erreichen möchte. Oft scheint es mir angebracht, Nebenwirkungen zu haben, und ich kann nicht wirklich erkennen, wie ich das erreichen kann, was ich erreichen möchte, indem ich nur Werte zurückgebe. Außerdem habe ich oft (ich denke, das ist schrecklich) Methoden, die beides tun.

Aus diesen 133 positiven Stimmen schließe ich jedoch, dass derzeit reine funktionale Programmierung "gewinnt", da es zu einem Konsens wird, dass es überlegen ist, zu sagen, nicht fragen. Ist das richtig?

Daher versuche ich am Beispiel dieses von Antimustern geprägten Spiels Folgendes zu machen : Wenn ich es mit dem reinen Funktionsparadigma in Einklang bringen wollte - WIE ?!

Es erscheint mir vernünftig, einen Kampfzustand zu haben. Da dies ein rundenbasiertes Spiel ist, behalte ich die Kampfzustände in einem Wörterbuch (Multiplayer - es können viele Schlachten gleichzeitig von vielen Spielern gespielt werden). Immer wenn ein Spieler an der Reihe ist, rufe ich eine geeignete Methode für den Kampfstatus auf, die (a) den Status entsprechend ändert und (b) den Spielern Aktualisierungen zurückgibt, die in JSON serialisiert werden, und ihnen im Grunde nur mitteilt, was gerade auf der passiert ist Tafel. Ich nehme an, dass dies eine offensichtliche Verletzung BEIDER Prinzipien darstellt und gleichzeitig.

OK - Ich könnte eine Methode dazu bringen, einen Kampfzustand ZURÜCKZUZIEHEN, anstatt ihn zu ändern, wenn ich es wirklich wollte. Aber! Muss ich dann alles im Kampfzustand unnötig kopieren, um einen völlig neuen Zustand zurückzugeben, anstatt ihn zu ändern?

Nun, wenn der Zug ein Angriff ist, könnte ich vielleicht nur eine HP mit aktualisierten Charakteren zurückgeben? Das Problem ist, dass es nicht so einfach ist: Spielregeln, ein Zug kann und wird oft viel mehr Auswirkungen haben, als nur einen Teil der HP eines Spielers zu entfernen. Dies kann zum Beispiel den Abstand zwischen Zeichen vergrößern, Spezialeffekte anwenden usw. usw.

Es scheint mir so viel einfacher zu sein, nur den vorhandenen Status zu ändern und Updates zurückzugeben ...

Aber wie würde ein erfahrener Ingenieur das angehen?

Gaazkam
quelle
9
Jedem Paradigma zu folgen ist ein sicherer Weg zum Scheitern. Politik sollte Intelligenz niemals übertreffen. Die Lösung eines Problems sollte vom Problem abhängen und nicht von Ihrer religiösen Überzeugung, ein Problem zu lösen.
John Douma
1
Ich habe hier noch nie eine Frage zu etwas gestellt bekommen, was ich vorher gesagt habe. Ich fühle mich geehrt. :)
David Arno

Antworten:

14

Wie die meisten Programmier-Aphorismen opfert "Tell, Don't Ask" Klarheit, um Kürze zu gewinnen. Es ist überhaupt nicht beabsichtigt, nicht nach den Ergebnissen einer Berechnung zu fragen, sondern nach den Eingaben einer Berechnung. "Nicht abrufen, dann berechnen, dann festlegen, aber es ist in Ordnung, einen Wert aus einer Berechnung zurückzugeben", ist nicht ganz so ausgeprägt.

Früher war es üblich, dass Leute einen Getter aufriefen, einige Berechnungen anstellten und dann einen Setter mit dem Ergebnis aufriefen. Dies ist ein klares Zeichen dafür, dass Ihre Berechnung tatsächlich zu der Klasse gehört, für die Sie den Getter aufgerufen haben. "Tell, don't ask" wurde geprägt, um die Leute daran zu erinnern, nach diesem Anti-Muster Ausschau zu halten, und es funktionierte so gut, dass jetzt einige Leute denken, dass dieser Teil offensichtlich ist und sie nach anderen Arten von "Fragen" suchen beseitigen. Der Aphorismus wird jedoch nur in dieser einen Situation sinnvoll angewendet.

Reine Funktionsprogramme litten nie unter diesem genauen Anti-Muster, aus dem einfachen Grund, dass es keine Setter in diesem Stil gibt. Das allgemeinere (und schwieriger zu erkennende) Problem, verschiedene semantische Abstraktionsebenen nicht in derselben Funktion zu mischen, gilt jedoch für jedes Paradigma.

Karl Bielefeldt
quelle
Vielen Dank, dass Sie "Tell, don't Ask" richtig erklärt haben.
user949300
13

Sowohl Onkel Bob als auch David Arno (der Autor des Zitats, das Sie hatten) haben wichtige Lehren, die wir aus dem, was sie geschrieben haben, ziehen können. Ich denke, es lohnt sich, die Lektion zu lernen und dann zu extrapolieren, was das für Sie und Ihr Projekt wirklich bedeutet.

Erstens: Onkel Bobs Lektion

Onkel Bob macht darauf aufmerksam, dass je mehr Argumente Sie in Ihrer Funktion / Methode haben, desto mehr Entwickler müssen es verstehen. Diese kognitive Belastung ist nicht kostenlos, und wenn Sie nicht mit der Reihenfolge der Argumente usw. übereinstimmen, erhöht sich die kognitive Belastung nur.

Das ist eine Tatsache, menschlich zu sein. Ich denke, der Hauptfehler in Onkel Bobs Clean Code-Buch ist die Aussage "Die ideale Anzahl von Argumenten für eine Funktion ist Null" . Minimalismus ist großartig, bis es nicht mehr so ​​ist. So wie Sie in Calculus nie an Ihre Grenzen stoßen, erreichen Sie auch nie den "idealen" Code - und sollten dies auch nicht tun.

Wie Albert Einstein sagte: "Alles sollte so einfach sein, wie es kann, aber nicht einfacher".

Zweitens: David Arnos Lektion

Die beschriebene Art der Entwicklung von David Arno ist eher eine funktionale Stilentwicklung als eine objektorientierte . Funktionscode skaliert jedoch viel besser als herkömmliche objektorientierte Programmierung. Warum? Wegen der Verriegelung. Jeder Zeitpunkt, an dem ein Objekt veränderlich ist, birgt das Risiko von Rennbedingungen oder Sperrkonflikten.

Da das Funktionsmodell hochkonkurrierende Systeme geschrieben hat, die in Simulationen und anderen serverseitigen Anwendungen verwendet werden, wirkt es Wunder. Ich kann die Verbesserungen bestätigen, die der Ansatz erzielt hat. Es ist jedoch ein ganz anderer Entwicklungsstil mit unterschiedlichen Anforderungen und Redewendungen.

Entwicklung ist eine Reihe von Kompromissen

Sie kennen Ihre Anwendung besser als jeder von uns. Möglicherweise benötigen Sie nicht die Skalierbarkeit, die mit der funktionalen Stilprogrammierung einhergeht. Es gibt eine Welt zwischen den beiden oben aufgeführten Idealen. Diejenigen von uns, die sich mit Systemen befassen, die einen hohen Durchsatz und eine lächerliche Parallelität bewältigen müssen, tendieren zum Ideal der funktionalen Programmierung.

Sie können jedoch Datenobjekte verwenden, um die Informationen zu speichern, die Sie an eine Methode übergeben müssen. Das hilft bei dem Problem der kognitiven Belastung, das Onkel Bob ansprach, während es gleichzeitig das funktionale Ideal unterstützt, das David Arno ansprach.

Ich habe an beiden Desktopsystemen mit eingeschränkter Parallelität und Simulationssoftware mit hohem Durchsatz gearbeitet. Sie haben sehr unterschiedliche Bedürfnisse. Ich kann gut geschriebenen objektorientierten Code zu schätzen wissen, der sich an dem Konzept des Versteckens von Daten orientiert, mit dem Sie vertraut sind. Es funktioniert für mehrere Anwendungen. Es funktioniert jedoch nicht bei allen.

Wer hat recht Nun, David hat in diesem Fall mehr Recht als Onkel Bob. Der grundlegende Punkt, den ich hier unterstreichen möchte, ist jedoch, dass eine Methode so viele Argumente haben sollte, wie sinnvoll sind.

Berin Loritsch
quelle
Es gibt Parallelen. Verschiedene Schlachten können parallel abgearbeitet werden. Aber ja: Eine einzelne Schlacht muss gesperrt werden, während sie abgearbeitet wird.
Gaazkam
Ja, ich meinte, dass die Leser (Schnitter in Ihrer Analogie) ihre (die Sämann-) Schriften lesen würden. Trotzdem habe ich mir einige Dinge angesehen, die ich in der Vergangenheit geschrieben habe, und entweder etwas neu gelernt oder bin mit meinem früheren Ich nicht einverstanden. Wir alle lernen und entwickeln uns weiter, und das ist der Grund Nummer eins, warum Sie immer darüber nachdenken sollten, wie und ob Sie etwas anwenden, was Sie gelernt haben.
Berin Loritsch
8

OK - Ich könnte eine Methode dazu bringen, einen Kampfzustand ZURÜCKZUZIEHEN, anstatt ihn zu ändern, wenn ich es wirklich wollte.

Ja, das ist die Idee.

Muss ich dann alles im Kampfzustand kopieren, um einen völlig neuen Zustand wiederherzustellen, anstatt ihn zu ändern?

Nein. Ihr "Kampfzustand" könnte als unveränderliche Datenstruktur modelliert werden, die andere unveränderliche Datenstrukturen als Bausteine ​​enthält, die möglicherweise in einigen Hierarchien unveränderlicher Datenstrukturen verschachtelt sind.

Es könnte also Teile des Kampfzustands geben, die während einer Runde nicht geändert werden müssen, und andere, die geändert werden müssen. Die Teile, die sich nicht ändern, müssen nicht kopiert werden, da sie unveränderlich sind. Es muss lediglich ein Verweis auf diese Teile kopiert werden, ohne dass die Gefahr von Nebenwirkungen besteht. Das funktioniert am besten in Sprachumgebungen mit Speicherbereinigung.

Google für "Efficient Immutable Data Structures", und Sie werden sicherlich einige Referenzen finden, wie dies im Allgemeinen funktioniert.

Es scheint mir so viel einfacher zu sein, nur den vorhandenen Status zu ändern und Updates zurückzugeben.

Bei bestimmten Problemen kann dies in der Tat einfacher sein. Spiele und rundenbasierte Simulationen können in diese Kategorie fallen, wenn sich ein großer Teil des Spielzustands von einer Runde zur nächsten ändert. Die Wahrnehmung dessen, was wirklich "einfacher" ist, ist jedoch bis zu einem gewissen Grad subjektiv und hängt auch stark davon ab, an was die Menschen gewöhnt sind.

Doc Brown
quelle
8

Als Autor des Kommentars sollte ich es hier klarstellen, da es natürlich mehr als die vereinfachte Version gibt, die mein Kommentar anbietet.

AFAIK Diese beiden Prinzipien sind grundsätzlich inkompatibel und stehen in der Nähe von Gegensätzen: Rein funktional kann als "nur Rückgabewerte, keine Nebenwirkungen" zusammengefasst werden, während tell, nicht ask als "nichts zurückgeben" zusammengefasst werden kann. nur Nebenwirkungen haben ".

Um ehrlich zu sein, finde ich das eine sehr merkwürdige Verwendung des Begriffs "erzählen, nicht fragen". Ich habe also vor ein paar Jahren gelesen, was Martin Fowler zu diesem Thema gesagt hat, und das war aufschlussreich . Der Grund, warum ich es seltsam fand, ist, dass "Tell Don't Ask" gleichbedeutend mit Abhängigkeitsinjektion in meinem Kopf ist und die reinste Form der Abhängigkeitsinjektion alles übergibt, was eine Funktion über ihre Parameter benötigt.

Aber es scheint, dass die Bedeutung, die ich für "Tell Don't Ask" verwende, darin besteht, Fowlers OO-fokussierte Definition zu nehmen und sie paradigmatischer zu machen. Ich glaube, dass das Konzept dabei zu seinen logischen Schlussfolgerungen führt.

Kehren wir zu einfachen Anfängen zurück. Wir haben "Klumpen von Logik" (Prozeduren) und wir haben globale Daten. Prozeduren lesen diese Daten direkt, um darauf zuzugreifen. Wir haben ein einfaches "Frage" -Szenario.

Wind vorwärts ein bisschen. Wir haben jetzt Objekte und Methoden. Diese Daten müssen nicht mehr global sein, sondern können über den Konstruktor übergeben und im Objekt enthalten werden. Und dann haben wir Methoden, die auf diese Daten einwirken. Jetzt haben wir also "Tell, Don't Ask", wie Fowler es beschreibt. Dem Objekt werden seine Daten mitgeteilt. Diese Methoden müssen den globalen Bereich nicht mehr nach ihren Daten fragen. Aber hier ist der Haken: Dies ist aus meiner Sicht immer noch nicht wahr "Tell, Don't Ask", da diese Methoden immer noch den Objektbereich abfragen müssen. Dies ist eher ein Szenario, in dem ich "erzählen, dann fragen" möchte.

Lassen Sie sich also auf den modernen Tag ein, werfen Sie den "It's OO All-Way-Down" -Ansatz ab und leihen Sie sich einige Prinzipien aus der funktionalen Programmierung. Beim Aufruf einer Methode werden ihr nun alle Daten über ihre Parameter zugeführt. Es kann (und wurde) argumentiert: "Was ist der Sinn, das kompliziert nur den Code?" Und ja, die Übergabe von Parametern, Daten, auf die über den Gültigkeitsbereich des Objekts zugegriffen werden kann, erhöht die Komplexität des Codes. Das Speichern dieser Daten in einem Objekt, anstatt sie global zugänglich zu machen, erhöht jedoch auch die Komplexität. Dennoch würden nur wenige argumentieren, dass globale Variablen immer besser sind, weil sie einfacher sind. Der Punkt ist, dass die Vorteile, die "sagen, nicht fragen" bringt, die Komplexität der Einschränkung des Anwendungsbereichs überwiegen. Dies gilt mehr für die Übergabe über Parameter als für die Einschränkung des Bereichs auf das Objekt.private staticund übergeben Sie alles, was es braucht, über die Parameter, und jetzt kann man sich darauf verlassen, dass diese Methode nicht heimlich auf Dinge zugreift, die sie nicht sollten. Außerdem wird empfohlen, die Methode klein zu halten, da sonst die Parameterliste außer Kontrolle gerät. Außerdem werden Schreibmethoden empfohlen, die den Kriterien für "reine Funktionen" entsprechen.

Also sehe ich "rein funktional" und "erzählen, nicht fragen" nicht als gegensätzlich. Ersteres ist für mich die einzige vollständige Umsetzung des Letzteren. Fowlers Ansatz ist nicht vollständig.

Aber es ist wichtig, sich daran zu erinnern, dass diese "vollständige Implementierung von Tell Don't Ask" wirklich ein Ideal ist, dh Pragmatismus muss ins Spiel kommen, wenn wir weniger idealistisch werden und ihn daher falsch als den einzig möglichen richtigen Ansatz behandeln. Nur sehr wenige Apps können sogar annähernd 100% nebenwirkungsfrei sein, aus dem einfachen Grund, dass sie nichts Sinnvolles tun würden, wenn sie wirklich nebenwirkungsfrei wären. Wir müssen den Status ändern, wir brauchen IO usw., damit die App nützlich ist. Und in solchen Fällen müssen Methoden Nebenwirkungen verursachen und können daher nicht rein sein. Als Faustregel gilt jedoch, diese "unreinen" Methoden auf ein Minimum zu beschränken. nur haben sie Nebenwirkungen, weil sie müssen, anstatt als die Norm.

Es erscheint mir vernünftig, einen Kampfzustand zu haben. Da dies ein rundenbasiertes Spiel ist, behalte ich die Kampfzustände in einem Wörterbuch (Multiplayer - es können viele Schlachten gleichzeitig von vielen Spielern gespielt werden). Immer wenn ein Spieler an der Reihe ist, rufe ich eine geeignete Methode für den Kampfstatus auf, die (a) den Status entsprechend ändert und (b) den Spielern Aktualisierungen zurückgibt, die in JSON serialisiert werden, und ihnen im Grunde nur mitteilt, was gerade auf der passiert ist Tafel.

Es erscheint mir mehr als vernünftig, einen Kampfzustand zu haben; es scheint wesentlich. Der gesamte Zweck eines solchen Codes besteht darin, Anforderungen zum Ändern des Status zu verarbeiten, diese Statusänderungen zu verwalten und sie zurückzumelden. Sie könnten diesen Zustand global behandeln, Sie könnten ihn in einzelnen Spielerobjekten halten oder Sie könnten ihn um eine Reihe von reinen Funktionen herumgeben. Welches Sie wählen, hängt davon ab, welches für Ihr bestimmtes Szenario am besten geeignet ist. Der globale Status vereinfacht das Design des Codes und ist schnell, was eine wichtige Voraussetzung für die meisten Spiele ist. Aber es erschwert die Wartung, das Testen und das Debuggen des Codes. Durch eine Reihe von reinen Funktionen wird der Code komplexer zu implementieren und es besteht die Gefahr, dass er durch übermäßiges Kopieren von Daten zu langsam wird. Aber es ist am einfachsten zu testen und zu warten. Der "OO-Ansatz" liegt auf halbem Weg dazwischen.

Der Schlüssel ist: Es gibt keine perfekte Lösung, die immer funktioniert. Das Ziel von reinen Funktionen ist es, Ihnen zu helfen, "in die Grube des Erfolgs zu fallen". Aber wenn diese Grube aufgrund der Komplexität, die sie für den Code mit sich bringen kann, so flach ist, dass Sie nicht so sehr darauf hereinfallen, als dass Sie darüber stolpern, dann ist dies nicht der richtige Ansatz für Sie. Streben Sie das Ideal an, aber seien Sie pragmatisch und hören Sie auf, wenn dieses Ideal diesmal nicht der richtige Ort ist.

Und zum Schluss noch einmal: Reine Funktionen und "Tell, Don't Ask" sind überhaupt keine Gegensätze.

David Arno
quelle
5

Für alles, was jemals gesagt wurde, gibt es einen Kontext, in den man diese Aussage stellen kann, die es absurd macht.

Bildbeschreibung hier eingeben

Onkel Bob ist völlig falsch, wenn Sie den Null-Argument-Rat als Voraussetzung nehmen. Er hat vollkommen recht, wenn Sie das so verstehen, dass jedes zusätzliche Argument den Code schwerer lesbar macht. Das hat einen Preis. Sie fügen Funktionen keine Argumente hinzu, da sie dadurch einfacher zu lesen sind. Sie fügen Funktionen Argumente hinzu, weil Ihnen kein guter Name einfällt, der die Abhängigkeit von diesem Argument deutlich macht.

Zum Beispiel pi()ist eine perfekte Funktion, wie sie ist. Warum? Weil es mir egal ist, wie oder ob es berechnet wurde. Oder wenn es e oder sin () verwendet, um zu der Zahl zu gelangen, die es zurückgibt. Mir geht es gut, denn der Name sagt mir alles, was ich wissen muss.

Allerdings sagt mir nicht jeder Name alles, was ich wissen muss. Einige Namen enthalten keine wichtigen Informationen zum Verständnis, die das Verhalten der Funktion steuern, wie dies auch offen gelegte Argumente tun. Genau das macht es einfacher, über den funktionalen Stil der Programmierung nachzudenken.

Ich kann Dinge unveränderlich und frei von Nebenwirkungen halten, und das ganz im Sinne von OOP. Return ist einfach ein Mechaniker, mit dem Werte für die nächste Prozedur auf dem Stapel belassen werden. Sie können genauso unveränderlich bleiben, indem Sie Ausgabeports verwenden, um Werte an andere unveränderliche Dinge zu übermitteln, bis Sie den letzten Ausgabeport treffen, der schließlich etwas ändern muss, damit die Benutzer ihn lesen können. Das gilt für jede Sprache, ob funktional oder nicht.

Behaupten Sie also bitte nicht, dass funktionale Programmierung und objektorientierte Programmierung "grundsätzlich inkompatibel" sind. Ich kann Objekte in meinen Funktionsprogrammen verwenden und ich kann reine Funktionen in meinen OO-Programmen verwenden.

Das Mischen ist jedoch mit Kosten verbunden: Erwartungen. Sie können den Mechanismen beider Paradigmen treu folgen und dennoch Verwirrung stiften. Einer der Vorteile der Verwendung einer funktionalen Sprache besteht darin, dass Nebenwirkungen, die vorhanden sein müssen, um eine Ausgabe zu erhalten, an einem vorhersehbaren Ort auftreten. Es sei denn natürlich, auf ein veränderliches Objekt wird nicht diszipliniert zugegriffen. Dann zerfällt, was Sie in dieser Sprache für gegeben hielten.

Ebenso können Sie Objekte mit reinen Funktionen unterstützen, Sie können Objekte entwerfen, die unveränderlich sind. Das Problem ist, wenn Sie nicht signalisieren, dass die Funktionen rein oder die Objekte unveränderlich sind, erhalten die Benutzer keinen Vorteil aus diesen Funktionen, bis sie viel Zeit mit dem Lesen des Codes verbracht haben.

Dies ist keine neue Ausgabe. Seit Jahren codieren Menschen prozedural in "OO-Sprachen" und denken, sie tun OO, weil sie eine "OO-Sprache" verwenden. Nur wenige Sprachen können Sie so gut davon abhalten, sich in den Fuß zu schießen. Damit diese Ideen funktionieren, müssen sie in dir leben.

Beide bieten gute Funktionen. Sie können beides tun. Wenn Sie mutig genug sind, sie zu mischen, kennzeichnen Sie sie bitte deutlich.

kandierte_orange
quelle
0

Ich kämpfe manchmal darum, alle Regeln verschiedener Paradigmen zu verstehen. Sie sind manchmal im Widerspruch zueinander, wie sie in dieser Situation sind.

OOP ist ein zwingendes Paradigma, bei dem es darum geht, mit einer Schere in der Welt zu laufen, in der gefährliche Dinge passieren.

FP ist ein Funktionsparadigma, in dem man absolute Sicherheit in der reinen Berechnung findet. Hier passiert nichts.

Alle Programme müssen jedoch eine Brücke in die imperative Welt schlagen, um nützlich zu sein. Also funktionaler Kern, zwingende Hülle .

Die Dinge werden verwirrend, wenn Sie unveränderliche Objekte definieren (Objekte, deren Befehle eine geänderte Kopie zurückgeben, anstatt tatsächlich zu mutieren). Sie sagen sich: "Das ist OOP" und "Ich definiere das Objektverhalten." Sie denken an das bewährte Tell, Don't Ask-Prinzip zurück. Das Problem ist, dass Sie es auf das falsche Gebiet anwenden.

Die Bereiche sind völlig unterschiedlich und folgen unterschiedlichen Regeln. Der Funktionsbereich baut sich bis zu dem Punkt auf, an dem Nebenwirkungen in die Welt entlassen werden sollen. Damit diese Effekte freigegeben werden können, müssen alle Daten, die in einem imperativen Objekt enthalten wären (wäre dies so geschrieben worden!), In der imperativen Shell verfügbar sein. Ohne Zugriff auf diese Daten, die in einer anderen Welt durch Kapselung verborgen worden wären, kann es nicht funktionieren. Es ist rechnerisch unmöglich.

Wenn Sie unveränderliche Objekte schreiben (was Clojure persistente Datenstrukturen nennt), denken Sie also daran, dass Sie sich in der funktionalen Domäne befinden. Werfen Sie Tell, Don't Ask aus dem Fenster und lassen Sie es erst dann im Haus zurück, wenn Sie das imperative Reich wieder betreten.

Mario T. Lanza
quelle