Die funktionale Programmierung umfasst viele verschiedene Techniken. Einige Techniken sind gut mit Nebenwirkungen. Ein wichtiger Aspekt ist jedoch das Gleichungsdenken : Wenn ich eine Funktion mit demselben Wert aufrufe, erhalte ich immer das gleiche Ergebnis. So kann ich einen Funktionsaufruf durch den Rückgabewert ersetzen und ein gleichwertiges Verhalten erhalten. Dies erleichtert das Nachdenken über das Programm, insbesondere beim Debuggen.
Sollte die Funktion Nebenwirkungen haben, gilt dies nicht ganz. Der Rückgabewert entspricht nicht dem Funktionsaufruf, da der Rückgabewert keine Nebenwirkungen enthält.
Die Lösung wird unter Verwendung von stoppen Nebenwirkungen und Codieren dieser Effekte im Rückgabewert . Unterschiedliche Sprachen haben unterschiedliche Effektsysteme. Beispielsweise verwendet Haskell Monaden, um bestimmte Effekte wie E / A oder Zustandsmutation zu codieren. Die C / C ++ / Rust-Sprachen verfügen über ein Typsystem, das die Mutation einiger Werte nicht zulassen kann.
In einer imperativen Sprache print("foo")
druckt eine Funktion etwas und gibt nichts zurück. In einer reinen Funktionssprache wie Haskell nimmt eine print
Funktion auch ein Objekt, das den Zustand der Außenwelt darstellt, und gibt ein neues Objekt zurück, das den Zustand darstellt, nachdem diese Ausgabe ausgeführt wurde. Ähnliches wie newState = print "foo" oldState
. Ich kann aus dem alten Zustand so viele neue Zustände erstellen, wie ich möchte. Es wird jedoch immer nur eine von der Hauptfunktion verwendet. Ich muss also die Zustände aus mehreren Aktionen sequenzieren, indem ich die Funktionen verkette. Zum Drucken foo bar
könnte ich so etwas sagen print "bar" (print "foo" originalState)
.
Wenn kein Ausgabestatus verwendet wird, führt Haskell die Aktionen, die zu diesem Status führen, nicht aus, da es sich um eine faule Sprache handelt. Umgekehrt ist diese Faulheit nur möglich, weil alle Effekte explizit als Rückgabewerte codiert sind.
Beachten Sie, dass Haskell die einzige häufig verwendete Funktionssprache ist, die diese Route verwendet. Andere funktionale Sprachen inkl. Die Lisp-Familie, die ML-Familie und neuere funktionale Sprachen wie Scala entmutigen, lassen aber immer noch Nebenwirkungen zu - sie könnten als imperativ-funktionale Sprachen bezeichnet werden.
Die Verwendung von Nebenwirkungen für E / A ist wahrscheinlich in Ordnung. Oft werden E / A (außer Protokollierung) nur an der äußeren Grenze Ihres Systems ausgeführt. Innerhalb Ihrer Geschäftslogik findet keine externe Kommunikation statt. Es ist dann möglich, den Kern Ihrer Software in einem reinen Stil zu schreiben, während unreine E / A in einer äußeren Hülle ausgeführt werden. Dies bedeutet auch, dass der Kern zustandslos sein kann.
Staatenlosigkeit hat eine Reihe praktischer Vorteile, wie z. B. eine erhöhte Vernünftigkeit und Skalierbarkeit. Dies ist sehr beliebt für Webanwendungs-Backends. Jeder Status wird außerhalb in einer gemeinsam genutzten Datenbank gespeichert. Dies erleichtert den Lastausgleich: Ich muss keine Sitzungen an einen bestimmten Server binden. Was ist, wenn ich mehr Server benötige? Fügen Sie einfach eine weitere hinzu, da dieselbe Datenbank verwendet wird. Was ist, wenn ein Server abstürzt? Ich kann ausstehende Anforderungen auf einem anderen Server wiederholen. Natürlich gibt es noch Status - in der Datenbank. Aber ich habe es explizit gemacht und extrahiert und könnte intern einen rein funktionalen Ansatz verwenden, wenn ich möchte.
Keine Programmiersprache beseitigt Nebenwirkungen. Ich denke, es ist besser zu sagen, dass deklarative Sprachen Nebenwirkungen enthalten, während imperative Sprachen dies nicht tun. Ich bin mir jedoch nicht so sicher, ob eines dieser Gespräche über Nebenwirkungen den grundlegenden Unterschied zwischen den beiden Arten von Sprachen zum Ausdruck bringt, und das scheint wirklich das zu sein, wonach Sie suchen.
Ich denke, es hilft, den Unterschied anhand eines Beispiels zu veranschaulichen.
Die obige Codezeile kann in praktisch jeder Sprache geschrieben werden. Wie können wir also feststellen, ob wir eine imperative oder deklarative Sprache verwenden? Wie unterscheiden sich die Eigenschaften dieser Codezeile in den beiden Sprachklassen?
In einer imperativen Sprache (C, Java, Javascript usw.) repräsentiert diese Codezeile lediglich einen Schritt in einem Prozess. Es sagt nichts über die fundamentale Natur eines der Werte aus. Es sagt uns, dass im Moment nach dieser Codezeile (aber vor der nächsten Zeile)
a
gleichb
plus ist,c
aber es sagt uns nichtsa
im weiteren Sinne.In einer deklarativen Sprache (Haskell, Scheme, Excel usw.) sagt diese Codezeile viel mehr aus. Es stellt eine unveränderliche Beziehung zwischen
a
und den beiden anderen Objekten her, so dass immer der Falla
gleichb
plus istc
. Beachten Sie, dass ich Excel in die Liste der deklarativen Sprachen aufgenommen habe, da selbst wennb
oder wennc
sich der Wert ändert, die Tatsache bestehen bleibt, dassa
sie der Summe entspricht.Meiner Meinung nach dieser , keine Nebenwirkungen oder Zustand, ist das, was die beiden Arten von Sprachen unterscheidet. In einer imperativen Sprache sagt eine bestimmte Codezeile nichts über die Gesamtbedeutung der betreffenden Variablen aus. Mit anderen Worten,
a = b + c
bedeutet nur, dass für einen sehr kurzen Momenta
die Summe vonb
und gleich warc
.In deklarativen Sprachen legt jede Codezeile eine grundlegende Wahrheit fest, die während der gesamten Laufzeit des Programms existiert.
a = b + c
Sagt Ihnen in diesen Sprachen, dass unabhängig davon, was in einer anderen Codezeile passiert,a
immer die Summe vonb
und gleich istc
.quelle