Was sind die Vorteile der referenziellen Transparenz in der Programmierung ?
RT macht einen der Hauptunterschiede zwischen funktionalem und imperativem Paradigma aus und wird häufig von Befürwortern des funktionalen Paradigmas als klarer Vorteil gegenüber dem imperativen Paradigma verwendet. Bei all ihren Bemühungen erklären diese Befürworter jedoch nie, warum dies für mich als Programmierer von Vorteil ist .
Sicher, sie werden ihre akademischen Erklärungen dazu haben, wie "rein" und "elegant" es ist, aber wie macht es es besser als ein weniger "reiner" Code? Was bringt es mir bei meiner täglichen Programmierung?
Hinweis: Dies ist kein Duplikat von Was ist referenzielle Transparenz? Letztere befasst sich mit dem Thema , was ist RT, während diese Frage seine Vorteile adressses (die möglicherweise nicht so intuitiv sein).
quelle
Antworten:
Der Vorteil ist, dass reine Funktionen Ihren Code leichter verständlich machen. Mit anderen Worten, Nebenwirkungen erhöhen die Komplexität Ihres Codes.
Nehmen Sie ein Beispiel für die
computeProductPrice
Methode.Ein reines Verfahren würden Sie für eine Produktmenge bitten, eine Währung, usw. Sie wissen , dass , wenn die Methode mit den gleichen Argumenten aufgerufen wird, es wird immer das gleiche Ergebnis.
Eine nicht reine Methode ist komplexer zu verwenden und zu debuggen. Da dies vom Zustand der Variablen abhängt, die keine Argumente sind und möglicherweise geändert werden, kann dies dazu führen, dass beim mehrmaligen Aufrufen unterschiedliche Ergebnisse erzielt werden oder dass nicht dasselbe Verhalten auftritt, wenn die Variablen überhaupt nicht oder zu früh oder zu spät aufgerufen werden.
Beispiel
Stellen Sie sich vor, es gibt eine Methode im Framework, die eine Zahl analysiert:
Es hat keine referenzielle Transparenz, da es von Folgendem abhängt:
Die Umgebungsvariable, die das Nummerierungssystem angibt, also Base 10 oder etwas anderes.
Die Variable in der
math
Bibliothek, die die Genauigkeit der zu analysierenden Zahlen angibt. So mit dem Wert1
, den String Parsen"12.3456"
geben12.3
.Die Kultur, die die erwartete Formatierung definiert. Zum Beispiel mit
fr-FR
, Parsing"12.345"
geben12345
, weil der Charakter Trennung sollte sein,
, nicht.
Stellen Sie sich vor, wie einfach oder schwierig es wäre, mit einer solchen Methode zu arbeiten. Mit der gleichen Eingabe können Sie je nach dem Moment, in dem Sie die Methode aufrufen, radikal unterschiedliche Ergebnisse erzielen, da irgendwo die Umgebungsvariable geändert oder die Kultur gewechselt oder eine andere Genauigkeit festgelegt wurde. Der nicht deterministische Charakter der Methode würde zu mehr Bugs und mehr Debugging-Alptraum führen. Als Antwort anzurufen
math.parse("12345")
und zu erhalten5349
, da einige parallele Codes Oktalzahlen parsen, ist nicht schön.Wie kann man diese offensichtlich kaputte Methode reparieren? Durch die Einführung referentieller Transparenz. Mit anderen Worten, indem Sie den globalen Zustand loswerden und alles an die Parameter der Methode verschieben:
Jetzt, da die Methode rein ist, wissen Sie, dass sie unabhängig vom Aufruf der Methode immer das gleiche Ergebnis für dieselben Argumente liefert.
quelle
packet = socket.recv()
referenziell transparent machen, verlieren Sie eher den Punkt der Funktion.invariant
? Entweder sind sie die gleichen wie füren_us
, in welchem Fall, warum sie sich die Mühe machen, oder sie entsprechen einem anderen Land, in welchem Fall, welchem und warum dieses stattdessenen_us
, oder sie haben ihre spezifischen Regeln, die ohnehin keinem Land entsprechen , das wäre nutzlos. Es gibt wirklich keine „wahre Antwort“ zwischen12,345.67
und12 345,67
: Alle „Standardregeln“ funktionieren für einige Länder und für andere nicht.12345
Analysiert als 1234512 345
oder12,345
oder12.345
ist ein Fehler.12.345
geparst als invariante Gleitkommazahl ergibt sich gemäß der Programmiersprachenkonvention immer ein Wert von 12,345. als Dezimaltrennzeichen. Zeichenfolgen werden nach ihren Unicode-Codepunkten und unter Berücksichtigung der Groß- und Kleinschreibung sortiert. Und so weiter.Fügen Sie häufig einem Punkt in Ihrem Code einen Haltepunkt hinzu und führen Sie die App im Debugger aus, um herauszufinden, was passiert? Wenn Sie dies tun, liegt dies hauptsächlich daran, dass Sie in Ihren Entwürfen keine referenzielle Transparenz (RT) verwenden. Und so muss der Code ausgeführt werden, um herauszufinden, was er tut.
Der springende Punkt bei RT ist, dass der Code sehr deterministisch ist, dh, Sie können den Code lesen und jedes Mal herausfinden, was er für denselben Satz von Eingaben tut. Sobald Sie anfangen, mutierende Variablen hinzuzufügen, von denen einige über eine einzelne Funktion hinausgehen, können Sie den Code nicht einfach lesen. Solcher Code muss entweder in Ihrem Kopf oder im Debugger ausgeführt werden, um herauszufinden, wie er wirklich funktioniert.
Je einfacher der Code zu lesen und zu begründen ist, desto einfacher ist es, Fehler zu pflegen und zu erkennen. Das spart Zeit und Geld für Sie und Ihren Arbeitgeber.
quelle
Die Leute werfen den Begriff "einfacher zu überlegen" herum, erklären aber nie, was das bedeutet. Betrachten Sie das folgende Beispiel:
Sind
result1
undresult2
gleich oder verschieden? Ohne referentielle Transparenz haben Sie keine Ahnung. Sie müssen tatsächlich den Body von lesenfoo
, um sicherzustellen, dass der Body von Funktionenfoo
aufgerufen wird, und so weiter.Die Menschen bemerken diese Belastung nicht, weil sie daran gewöhnt sind, aber wenn Sie ein oder zwei Monate lang in einer rein funktionalen Umgebung arbeiten, werden Sie sie spüren, und es ist eine riesige Sache .
Es gibt so viele Verteidigungsmechanismen, die Menschen einsetzen, um den Mangel an referentieller Transparenz zu umgehen. Für mein kleines Beispiel möchte ich vielleicht
result1
in Erinnerung bleiben , weil ich nicht wüsste, ob es sich ändern würde. Dann habe ich Code mit zwei Zuständen: vorherresult1
wurde gespeichert und nachher. Mit referentieller Transparenz kann ich es einfach neu berechnen, solange die Neuberechnung nicht zeitaufwändig ist.quelle
result1
undresult2
welche identisch sind. Ein weiterer wichtiger Aspekt ist, dass Sie sich beifoo("bar", 12)
referenzieller Transparenz nicht fragen müssen, ob dieser Aufruf irgendwo anders Effekte hervorgerufen hat (einige Variablen setzen - eine Datei löschen - was auch immer).Ich würde sagen: referentielle Transparenz ist nicht nur gut für die funktionale Programmierung, sondern für alle, die mit Funktionen arbeiten, weil sie dem Prinzip des geringsten Erstaunens folgt.
Sie haben eine Funktion und können besser darüber nachdenken, was sie tut, da Sie keine externen Faktoren berücksichtigen müssen. Für einen bestimmten Eingang ist der Ausgang immer der gleiche. Selbst in meiner imperativen Sprache versuche ich, diesem Paradigma so weit wie möglich zu folgen. Das nächste, was sich daraus automatisch ergibt, ist: kleine, leicht verständliche Funktionen anstelle der grausamen über 1000 Zeilenfunktionen, in denen ich manchmal laufe.
Diese großen Funktionen zaubern und ich habe Angst, sie zu berühren, weil sie auf spektakuläre Weise brechen können.
Reine Funktionen sind also nicht nur etwas für die funktionale Programmierung, sondern für jedes Programm.
quelle