Eine Art funktionale Programmier-Neuling-Frage hier:
Ich habe die Transkripte einiger von Rich Hickeys Vorträgen gelesen, und in einigen seiner bekannteren empfiehlt er, Warteschlangen als Alternative zum gegenseitigen Aufruf von Funktionen zu verwenden. (ZB in Design, Komposition und Leistung und in Simple Made Easy .)
Ich verstehe das in vielerlei Hinsicht nicht ganz:
Spricht er davon, Daten in eine Warteschlange zu stellen und sie dann von jeder Funktion verwenden zu lassen? Anstelle von Funktion A, die Funktion B aufruft, um ihre eigene Berechnung durchzuführen, muss Funktion B ihre Ausgabe in eine Warteschlange stellen und dann Funktion A darauf zugreifen lassen. Oder sprechen wir alternativ davon, Funktionen in eine Warteschlange zu stellen und sie dann sukzessive auf Daten anzuwenden (sicherlich nicht, da dies eine massive Mutation beinhalten würde, oder? Und auch die Multiplikation von Warteschlangen für Funktionen mit mehreren Aritäten oder wie Bäume oder ähnliches? )
Wie macht das die Sache einfacher? Meine Intuition wäre, dass diese Strategie mehr Komplexität schaffen würde, weil die Warteschlange eine Art Zustand wäre, und dann müssen Sie sich Sorgen machen, "was ist, wenn sich eine andere Funktion einschleicht und einige Daten in die Warteschlange stellt?"
Eine Antwort auf eine Implementierungsfrage zu SO legt nahe, dass die Idee eine Reihe verschiedener Warteschlangen erstellt. Jede Funktion stellt ihre Ausgabe in eine eigene Warteschlange (??). Das verwirrt mich aber auch, denn wenn Sie eine Funktion einmal ausführen, warum braucht sie dann eine Warteschlange für ihre Ausgabe, wenn Sie diese Ausgabe einfach nehmen und einen Namen als (var, atom, entry in a big) darauf setzen könnten Hash-Tabelle, was auch immer). Wenn eine Funktion dagegen mehrmals ausgeführt wird und Sie ihre Ausgabe in eine Warteschlange stellen, haben Sie sich selbst wieder den Status zugefügt, und Sie müssen sich Gedanken über die Reihenfolge machen, in der alles aufgerufen wird. Downstream-Funktionen werden weniger rein. usw.
Offensichtlich verstehe ich den Punkt hier nicht. Kann jemand etwas erklären?
quelle
Job
Objekt, verschieben ihn in eine Warteschlange und lassen einen oder mehrere Arbeitsthreads an dieser Warteschlange arbeiten. DerJob
schickt dannJob
nach Abschluss weitere s in die Warteschlange. Rückgabewerte werden in diesem Konzept durch Rückrufe ersetzt. Es ist ein Albtraum, Fehler zu beheben und zu überprüfen, da Ihnen ein Aufrufstapel fehlt, und aus demselben Grund effizient und flexibel.Antworten:
Es ist eher eine Entwurfsübung als eine allgemeine Empfehlung. Normalerweise stellen Sie keine Warteschlange zwischen all Ihren direkten Funktionsaufrufen. Das wäre lächerlich. Wenn Sie Ihre Funktionen jedoch nicht so gestalten, als ob eine Warteschlange zwischen den direkten Funktionsaufrufen eingefügt werden könnte, können Sie nicht zu Recht behaupten, wiederverwendbaren und zusammensetzbaren Code geschrieben zu haben. Das ist der Punkt, den Rich Hickey macht.
Dies ist beispielsweise ein Hauptgrund für den Erfolg von Apache Spark . Sie schreiben Code, der aussieht, als würde er direkte Funktionsaufrufe für lokale Sammlungen ausführen, und das Framework übersetzt diesen Code in die Weitergabe von Nachrichten an Warteschlangen zwischen Clusterknoten. Die Art von einfachem, zusammensetzbarem und wiederverwendbarem Codierungsstil, den Rich Hickey befürwortet, macht dies möglich.
quelle
Zu beachten ist, dass Sie mit der funktionalen Programmierung Funktionen indirekt über Mediatorobjekte miteinander verbinden können , die sich darum kümmern, Argumente für die Funktionen zu beschaffen und ihre Ergebnisse flexibel an Empfänger weiterzuleiten, die ihre Ergebnisse wünschen. Angenommen, Sie haben einen einfachen Direktaufrufcode, der wie in diesem Beispiel in Haskell aussieht:
Nun, mit Haskells
Applicative
Klasse und ihren<$>
und<*>
Operatoren können wir diesen Code mechanisch umschreiben:... wo jetzt
myThing
nicht mehr direkt angerufen wirdf
undg
sondern durch einige Vermittler des Typs verbunden wirdf
. Sof
könnte beispielsweise einStream
Typ sein, der von einer Bibliothek bereitgestellt wird, die eine Schnittstelle zu einem Warteschlangensystem bereitstellt. In diesem Fall hätten wir diesen Typ:Systeme wie dieses existieren. Tatsächlich können Sie Java 8-Streams als eine Version dieses Paradigmas betrachten. Sie erhalten folgenden Code:
Hier verwenden Sie folgende Funktionen:
t -> t.getType() == Transaction.GROCERY
comparing(Transaction::getValue).reversed()
Transaction::getId
toList()
... und anstatt dass sie sich direkt anrufen, verwenden Sie das
Stream
System, um zwischen ihnen zu vermitteln. In diesem Codebeispiel wird dieTransaction::getId
Funktion nicht direktStream
aufgerufen, sondern mit den Transaktionen, die zuvor überlebt habenfilter
. Sie können sich dieStream
Warteschlange als eine sehr minimale Art von Warteschlange vorstellen, die Funktionen indirekt koppelt und Werte zwischen ihnen weiterleitet.quelle