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.