Ich habe den Wikipedia-Artikel über reaktive Programmierung gelesen . Ich habe auch den kleinen Artikel über funktionale reaktive Programmierung gelesen . Die Beschreibungen sind ziemlich abstrakt.
- Was bedeutet funktionale reaktive Programmierung (FRP) in der Praxis?
- Woraus besteht reaktive Programmierung (im Gegensatz zu nicht reaktiver Programmierung?)?
Mein Hintergrund ist in imperativen / OO-Sprachen, daher wäre eine Erklärung, die sich auf dieses Paradigma bezieht, willkommen.
Antworten:
Wenn Sie ein Gefühl für FRP bekommen möchten, können Sie mit dem alten Fran-Tutorial von 1998 beginnen, das animierte Illustrationen enthält. Beginnen Sie für Artikel mit Functional Reactive Animation und folgen Sie den Links auf dem Publikationslink auf meiner Homepage und dem FRP- Link im Haskell-Wiki .
Persönlich denke ich gerne darüber nach, was FRP bedeutet, bevor ich mich mit der möglichen Implementierung befasse. (Code ohne Spezifikation ist eine Antwort ohne Frage und daher "nicht einmal falsch".) Daher beschreibe ich FRP nicht in Repräsentations- / Implementierungsbegriffen wie Thomas K in einer anderen Antwort (Grafiken, Knoten, Kanten, Brennen, Ausführung, usw). Es gibt viele mögliche Implementierungsstile, aber keine Implementierung sagt aus, was FRP ist .
Ich stimme mit Laurence Gs einfacher Beschreibung überein, dass es bei FRP um "Datentypen geht, die einen Wert 'über die Zeit' darstellen". Die konventionelle imperative Programmierung erfasst diese dynamischen Werte nur indirekt durch Zustand und Mutationen. Die gesamte Geschichte (Vergangenheit, Gegenwart, Zukunft) hat keine erstklassige Darstellung. Darüber hinaus können nur sich diskret entwickelnde Werte (indirekt) erfasst werden, da das imperative Paradigma zeitlich diskret ist. Im Gegensatz dazu erfasst FRP diese sich entwickelnden Werte direkt und hat keine Schwierigkeiten mit sich kontinuierlich entwickelnden Werten.
FRP ist auch insofern ungewöhnlich, als es gleichzeitig stattfindet, ohne das theoretische und pragmatische Rattennest zu verletzen, das die zwingende Parallelität plagt. Semantisch gesehen ist die Parallelität von FRP feinkörnig , bestimmt und kontinuierlich . (Ich spreche von Bedeutung, nicht von Implementierung. Eine Implementierung kann Parallelität oder Parallelität beinhalten oder nicht.) Semantische Determiniertheit ist sehr wichtig für das Denken, sowohl rigoros als auch informell. Parallelität erhöht die Komplexität der imperativen Programmierung enorm (aufgrund nichtdeterministischer Verschachtelung), ist jedoch in FRP mühelos.
Was ist FRP? Du hättest es selbst erfinden können. Beginnen Sie mit diesen Ideen:
Dynamische / sich entwickelnde Werte (dh Werte "über die Zeit") sind an sich erstklassige Werte. Sie können sie definieren und kombinieren, sie an und aus Funktionen übergeben. Ich habe diese Dinge "Verhalten" genannt.
Verhaltensweisen werden aus wenigen Grundelementen wie konstantem (statischem) Verhalten und Zeit (wie einer Uhr) und dann mit sequentieller und paralleler Kombination aufgebaut. n Verhaltensweisen werden kombiniert, indem eine n-fache Funktion (auf statische Werte) "punktweise" angewendet wird, dh kontinuierlich über die Zeit.
Um diskrete Phänomene zu berücksichtigen, müssen Sie eine andere Art (Familie) von "Ereignissen" haben, von denen jedes einen Strom (endlich oder unendlich) von Ereignissen aufweist. Jedem Vorkommen ist eine Zeit und ein Wert zugeordnet.
Spielen Sie einige Beispiele, um das kompositorische Vokabular zu erstellen, aus dem alle Verhaltensweisen und Ereignisse aufgebaut werden können. Dekonstruieren Sie weiter in Teile, die allgemeiner / einfacher sind.
Damit Sie wissen, dass Sie auf festem Grund stehen, geben Sie dem gesamten Modell eine kompositorische Grundlage unter Verwendung der Technik der Denotationssemantik. Dies bedeutet lediglich, dass (a) jeder Typ einen entsprechenden einfachen und präzisen mathematischen Typ von "Bedeutungen" hat und ( b) Jedes Grundelement und jeder Operator hat eine einfache und genaue Bedeutung in Abhängigkeit von der Bedeutung der Bestandteile. Mischen Sie niemals Implementierungsüberlegungen in Ihren Explorationsprozess. Wenn diese Beschreibung für Sie Kauderwelsch ist, konsultieren Sie (a) Denotationsdesign mit Typklassenmorphismen , (b) Push-Pull-funktionale reaktive Programmierung (Ignorieren der Implementierungsbits) und (c) die Wikibooks-Seite Denotational Semantics Haskell. Beachten Sie, dass die Denotationssemantik aus zwei Teilen besteht, von den beiden Gründern Christopher Strachey und Dana Scott: dem einfacheren und nützlicheren Strachey-Teil und dem schwierigeren und weniger nützlichen (für das Software-Design) Scott-Teil.
Wenn Sie sich an diese Grundsätze halten, werden Sie wahrscheinlich mehr oder weniger etwas im Sinne von FRP erhalten.
Woher habe ich diese Prinzipien? Beim Software-Design stelle ich immer die gleiche Frage: "Was bedeutet das?". Die denotationale Semantik gab mir einen genauen Rahmen für diese Frage, der zu meiner Ästhetik passt (im Gegensatz zur operativen oder axiomatischen Semantik, die mich beide unbefriedigt lässt). Also habe ich mich gefragt, was Verhalten ist. Ich erkannte bald, dass die zeitlich diskrete Natur der imperativen Berechnung eher eine Anpassung an einen bestimmten Maschinenstil als eine natürliche Beschreibung des Verhaltens selbst ist. Die einfachste genaue Beschreibung des Verhaltens, die ich mir vorstellen kann, ist einfach "Funktion der (kontinuierlichen) Zeit", das ist also mein Modell. Dieses Modell behandelt auf wunderbare Weise die kontinuierliche, deterministische Parallelität mit Leichtigkeit und Anmut.
Es war eine ziemliche Herausforderung, dieses Modell korrekt und effizient zu implementieren, aber das ist eine andere Geschichte.
quelle
In der reinen Funktionsprogrammierung treten keine Nebenwirkungen auf. Bei vielen Arten von Software (z. B. bei allen Benutzerinteraktionen) sind auf einer bestimmten Ebene Nebenwirkungen erforderlich.
Eine Möglichkeit, nebenwirkungsähnliches Verhalten zu erzielen und gleichzeitig einen funktionalen Stil beizubehalten, ist die Verwendung einer funktionalen reaktiven Programmierung. Dies ist die Kombination aus funktionaler Programmierung und reaktiver Programmierung. (Der Wikipedia-Artikel, auf den Sie verlinkt haben, handelt von letzterem.)
Die Grundidee hinter reaktiver Programmierung ist, dass es bestimmte Datentypen gibt, die einen Wert "über die Zeit" darstellen. Berechnungen, die diese sich im Laufe der Zeit ändernden Werte beinhalten, haben selbst Werte, die sich im Laufe der Zeit ändern.
Beispielsweise könnten Sie die Mauskoordinaten als ein Paar von Ganzzahl-über-Zeit-Werten darstellen. Nehmen wir an, wir hatten so etwas wie (das ist Pseudocode):
Zu jedem Zeitpunkt hätten x und y die Koordinaten der Maus. Im Gegensatz zur nicht reaktiven Programmierung müssen wir diese Zuordnung nur einmal vornehmen, und die x- und y-Variablen bleiben automatisch "auf dem neuesten Stand". Aus diesem Grund arbeiten reaktive Programmierung und funktionale Programmierung so gut zusammen: Durch reaktive Programmierung müssen keine Variablen mehr mutiert werden, und Sie können dennoch viel tun, was Sie mit variablen Mutationen erreichen können.
Wenn wir dann einige darauf basierende Berechnungen durchführen, sind die resultierenden Werte auch Werte, die sich im Laufe der Zeit ändern. Zum Beispiel:
In diesem Beispiel ist
minX
immer 16 kleiner als die x-Koordinate des Mauszeigers. Mit reaktivbewussten Bibliotheken könnte man dann etwas sagen wie:Ein 32x32-Feld wird um den Mauszeiger herum gezeichnet und verfolgt, wo immer er sich bewegt.
Hier ist ein ziemlich gutes Papier über funktionale reaktive Programmierung .
quelle
sqrt(x)
C mit Ihrem Makro aufrufe, berechnet das nursqrt(mouse_x())
und gibt mir ein Double zurück. In einem echten funktionellen reaktiven Systemsqrt(x)
würde ein neues "Double Over Time" zurückgegeben. Wenn Sie versuchen würden, ein FR-System mit zu simulieren#define
, müssten Sie Variablen zugunsten von Makros abschwören. FR-Systeme berechnen normalerweise auch nur dann Daten neu, wenn sie neu berechnet werden müssen. Die Verwendung von Makros würde bedeuten, dass Sie ständig alles neu bewerten, bis hin zu den Unterausdrücken.Eine einfache Möglichkeit, eine erste Vorstellung davon zu bekommen, wie es ist, besteht darin, sich vorzustellen, dass Ihr Programm eine Tabelle ist und alle Ihre Variablen Zellen sind. Wenn sich eine der Zellen in einer Tabelle ändert, ändern sich auch alle Zellen, die auf diese Zelle verweisen. Bei FRP ist es genauso. Stellen Sie sich nun vor, dass sich einige der Zellen von selbst ändern (oder eher von außen stammen): In einer GUI-Situation wäre die Position der Maus ein gutes Beispiel.
Das fehlt zwangsläufig ziemlich viel. Die Metapher bricht ziemlich schnell zusammen, wenn Sie tatsächlich ein FRP-System verwenden. Zum einen gibt es normalerweise auch Versuche, diskrete Ereignisse zu modellieren (z. B. wenn die Maus angeklickt wird). Ich schreibe das hier nur, um Ihnen eine Vorstellung davon zu geben, wie es ist.
quelle
Für mich geht es um 2 verschiedene Bedeutungen des Symbols
=
:x = sin(t)
bedeutet dasx
ein anderer Name fürsin(t)
. Schreibenx + y
ist also dasselbe wiesin(t) + y
. Funktionale reaktive Programmierung ist in dieser Hinsicht wie Mathematik: Wenn Sie schreibenx + y
, wird sie mit dem Wert berechnet,t
der zum Zeitpunkt der Verwendung vorliegt.x = sin(t)
ist eine Zuweisung: Dies bedeutet, dassx
der Wertsin(t)
gespeichert wird, der zum Zeitpunkt der Zuweisung genommen wurde.quelle
x = sin(t)
Mittelnx
ist der Wertsin(t)
für das gegebenet
. Es ist kein anderer Name fürsin(t)
als Funktion. Sonst wäre esx(t) = sin(t)
.2 + 3 = 5
odera**2 + b**2 = c**2
.OK, aus dem Hintergrundwissen und dem Lesen der Wikipedia-Seite, auf die Sie hingewiesen haben, scheint es, dass reaktive Programmierung so etwas wie Datenfluss-Computing ist, aber mit spezifischen externen "Stimuli", die eine Reihe von Knoten auslösen, um ihre Berechnungen auszulösen und durchzuführen.
Dies eignet sich beispielsweise sehr gut für das UI-Design, bei dem durch Berühren eines Steuerelements für die Benutzeroberfläche (z. B. des Lautstärkereglers einer Musikwiedergabeanwendung) möglicherweise verschiedene Anzeigeelemente und die tatsächliche Lautstärke der Audioausgabe aktualisiert werden müssen. Wenn Sie das Volumen ändern (z. B. einen Schieberegler), entspricht dies dem Ändern des Werts, der einem Knoten in einem gerichteten Diagramm zugeordnet ist.
Verschiedene Knoten mit Kanten von diesem "Volumenwert" -Knoten würden automatisch ausgelöst, und alle erforderlichen Berechnungen und Aktualisierungen würden natürlich die Anwendung durchlaufen. Die Anwendung "reagiert" auf den Benutzerreiz. Funktionale reaktive Programmierung wäre nur die Umsetzung dieser Idee in einer funktionalen Sprache oder allgemein innerhalb eines funktionalen Programmierparadigmas.
Wenn Sie mehr über "Dataflow Computing" erfahren möchten, suchen Sie in Wikipedia oder mit Ihrer bevorzugten Suchmaschine nach diesen beiden Wörtern. Die allgemeine Idee lautet: Das Programm ist ein gerichteter Graph von Knoten, die jeweils eine einfache Berechnung durchführen. Diese Knoten sind durch Diagrammverknüpfungen miteinander verbunden, die die Ausgaben einiger Knoten mit den Eingaben anderer Knoten versehen.
Wenn ein Knoten ausgelöst wird oder seine Berechnung durchführt, werden die entsprechenden Eingänge der an seine Ausgänge angeschlossenen Knoten "ausgelöst" oder "markiert". Jeder Knoten, bei dem alle Eingänge ausgelöst / markiert / verfügbar sind, wird automatisch ausgelöst. Das Diagramm kann implizit oder explizit sein, je nachdem, wie die reaktive Programmierung genau implementiert ist.
Knoten können als parallel ausgelöst betrachtet werden, werden jedoch häufig seriell oder mit begrenzter Parallelität ausgeführt (z. B. werden möglicherweise einige Threads ausgeführt). Ein berühmtes Beispiel war die Manchester Dataflow Machine , die (IIRC) eine getaggte Datenarchitektur verwendete, um die Ausführung von Knoten im Diagramm über eine oder mehrere Ausführungseinheiten zu planen. Das Datenfluss-Computing eignet sich ziemlich gut für Situationen, in denen das asynchrone Auslösen von Berechnungen, die zu Kaskaden von Berechnungen führen, besser funktioniert als der Versuch, die Ausführung von einer Uhr (oder von Uhren) steuern zu lassen.
Reaktive Programmierung importiert diese Idee der "Ausführungskaskade" und scheint das Programm datenflussartig zu denken, jedoch mit der Maßgabe, dass einige der Knoten mit der "Außenwelt" verbunden sind und die Ausführungskaskaden ausgelöst werden, wenn diese sensorischen -ähnliche Knoten ändern sich. Die Programmausführung würde dann wie etwas aussehen, das einem komplexen Reflexbogen entspricht. Das Programm kann zwischen Stimuli grundsätzlich sitzend sein oder nicht oder kann sich zwischen Reizen in einem grundsätzlich sitzenden Zustand niederlassen.
"nicht reaktive" Programmierung wäre eine Programmierung mit einer ganz anderen Sicht auf den Ausführungsfluss und die Beziehung zu externen Eingaben. Es ist wahrscheinlich etwas subjektiv, da die Leute wahrscheinlich versucht sind, etwas zu sagen, das auf externe Eingaben reagiert und auf sie "reagiert". Wenn man jedoch den Geist der Sache betrachtet, ist ein Programm, das eine Ereigniswarteschlange in einem festgelegten Intervall abfragt und alle gefundenen Ereignisse an Funktionen (oder Threads) sendet, weniger reaktiv (da es nur in einem festgelegten Intervall auf Benutzereingaben achtet). Auch hier ist es der Geist der Sache: Man kann sich vorstellen, eine Polling-Implementierung mit einem schnellen Polling-Intervall in ein System auf einer sehr niedrigen Ebene zu integrieren und darüber auf reaktive Weise zu programmieren.
quelle
Nachdem ich viele Seiten über FRP gelesen hatte, stieß ich endlich auf dieses aufschlussreiche Schreiben über FRP, das mir endlich verständlich machte, worum es bei FRP wirklich geht.
Ich zitiere unten Heinrich Apfelmus (Autor der reaktiven Banane).
Nach meinem Verständnis besteht ein FRP-Programm aus einer Reihe von Gleichungen:
j
ist diskret: 1,2,3,4 ...f
hängt davon ab,t
dass dies die Möglichkeit beinhaltet, externe Reize zu modellierenDer gesamte Status des Programms ist in Variablen eingekapselt
x_i
Die FRP-Bibliothek kümmert sich um die fortschreitende Zeit, mit anderen Worten,
j
umj+1
.Ich erkläre diese Gleichungen in diesem Video viel detaillierter .
BEARBEITEN:
Ungefähr zwei Jahre nach der ursprünglichen Antwort bin ich kürzlich zu dem Schluss gekommen, dass FRP-Implementierungen einen weiteren wichtigen Aspekt haben. Sie müssen (und tun dies normalerweise) ein wichtiges praktisches Problem lösen: die Ungültigmachung des Caches .
Die Gleichungen für
x_i
-s beschreiben einen Abhängigkeitsgraphen. Wenn einige derx_i
Änderungen zu einem bestimmten Zeitpunkt vorgenommen werden, müssenj
nicht alle anderenx_i'
Wertej+1
aktualisiert werden, sodass nicht alle Abhängigkeiten neu berechnet werden müssen, da einigex_i'
möglicherweise unabhängig von sindx_i
.Darüber hinaus können
x_i
-s, die sich ändern, schrittweise aktualisiert werden. Zum Beispiel lassen betrachtet die Karte Operationf=g.map(_+1)
in Scala, wof
undg
sindList
vonInts
. Hierf
entsprichtx_i(t_j)
undg
istx_j(t_j)
. Wenn ich nun ein Element voranstelleg
, wäre es verschwenderisch, diemap
Operation für alle Elemente in auszuführeng
. Einige FRP-Implementierungen (zum Beispiel reflex-frp ) zielen darauf ab, dieses Problem zu lösen. Dieses Problem wird auch als inkrementelles Rechnen bezeichnet.Mit anderen Worten, Verhaltensweisen (die
x_i
-s) in FRP können als zwischengespeicherte Berechnungen betrachtet werden. Es ist die Aufgabe der FRP-Engine, diese Cache-s (diex_i
-s) effizient ungültig zu machen und neu zu berechnen, wenn sich einige derf_i
-s ändern.quelle
j+1
" gibt. Denken Sie stattdessen an Funktionen der kontinuierlichen Zeit. Wie Newton, Leibniz und andere uns gezeigt haben, ist es oft sehr praktisch (und im wahrsten Sinne des Wortes "natürlich"), diese Funktionen unter Verwendung von Integralen und ODE-Systemen differenziell, aber kontinuierlich zu beschreiben. Andernfalls beschreiben Sie einen Approximationsalgorithmus (und einen schlechten) anstelle des Dings selbst.Das Papier Einfach effiziente funktionelle Reaktivität von Conal Elliott ( direktes PDF , 233 KB) ist eine ziemlich gute Einführung. Die entsprechende Bibliothek funktioniert ebenfalls.
Das Papier wird jetzt durch ein anderes Papier ersetzt, die funktionale reaktive Push-Pull-Programmierung ( direktes PDF , 286 KB).
quelle
Haftungsausschluss: Meine Antwort steht im Zusammenhang mit rx.js - einer Bibliothek für reaktive Programmierung für Javascript.
Bei der funktionalen Programmierung wenden Sie, anstatt jedes Element einer Sammlung zu durchlaufen, Funktionen höherer Ordnung (HoFs) auf die Sammlung selbst an. Die Idee hinter FRP ist also, anstatt jedes einzelne Ereignis zu verarbeiten, einen Strom von Ereignissen zu erstellen (implementiert mit einem beobachtbaren *) und stattdessen HoFs darauf anzuwenden. Auf diese Weise können Sie das System als Daten-Pipelines visualisieren, die Publisher mit Abonnenten verbinden.
Die Hauptvorteile der Verwendung eines Observable sind:
i) Es abstrahiert den Status von Ihrem Code, z. B. wenn Sie möchten, dass der Ereignishandler nur für jedes 'n'-te Ereignis ausgelöst wird oder nach den ersten' n 'Ereignissen nicht mehr ausgelöst wird. Wenn Sie erst nach den ersten 'n' Ereignissen mit dem Auslösen beginnen, können Sie einfach die HoFs (Filter, TakeUntil, Skip) verwenden, anstatt Zähler zu setzen, zu aktualisieren und zu überprüfen.
ii) Es verbessert die Codelokalität. Wenn 5 verschiedene Ereignishandler den Status einer Komponente ändern, können Sie deren Observable zusammenführen und stattdessen einen einzelnen Ereignishandler für das zusammengeführte Observable definieren, wodurch 5 Ereignishandler effektiv zu 1 kombiniert werden. Dies macht es sehr Es ist leicht zu überlegen, welche Ereignisse in Ihrem gesamten System eine Komponente beeinflussen können, da alles in einem einzigen Handler vorhanden ist.
Ein Iterable ist eine träge konsumierte Sequenz - jedes Element wird vom Iterator gezogen, wann immer es verwendet werden soll, und daher wird die Aufzählung vom Verbraucher gesteuert.
Ein Observable ist eine träge produzierte Sequenz - jedes Element wird an den Beobachter weitergeleitet, wenn es zur Sequenz hinzugefügt wird, und daher wird die Aufzählung vom Produzenten gesteuert.
quelle
Alter, das ist eine verdammt geniale Idee! Warum habe ich 1998 nichts davon erfahren? Wie auch immer, hier ist meine Interpretation des Fran- Tutorials. Vorschläge sind sehr willkommen. Ich denke darüber nach, eine darauf basierende Spiel-Engine zu starten.
Kurz gesagt: Wenn jede Komponente wie eine Zahl behandelt werden kann, kann das gesamte System wie eine mathematische Gleichung behandelt werden, oder?
quelle
Paul Hudaks Buch The Haskell School of Expression ist nicht nur eine gute Einführung in Haskell, sondern verbringt auch viel Zeit mit FRP. Wenn Sie ein Anfänger mit FRP sind, empfehle ich es sehr, um Ihnen einen Eindruck davon zu vermitteln, wie FRP funktioniert.
Es gibt auch eine neue Neufassung dieses Buches (veröffentlicht 2011, aktualisiert 2014), The Haskell School of Music .
quelle
Nach den vorherigen Antworten scheint es, dass wir mathematisch einfach in einer höheren Ordnung denken. Anstatt an einen Wert x vom Typ X zu denken, denken wir an eine Funktion x : T → X , wobei T die Art der Zeit ist, sei es die natürlichen Zahlen, die ganzen Zahlen oder das Kontinuum. Wenn wir nun y : = x + 1 in der Programmiersprache schreiben , meinen wir tatsächlich die Gleichung y ( t ) = x ( t ) + 1.
quelle
Verhält sich wie eine Tabelle wie angegeben. In der Regel basierend auf einem ereignisgesteuerten Framework.
Wie bei allen "Paradigmen" ist die Neuheit umstritten.
Aus meiner Erfahrung mit verteilten Flussnetzwerken von Akteuren kann es leicht zu einem allgemeinen Problem der Zustandskonsistenz im Netzwerk von Knoten kommen, dh Sie haben viel Schwingung und werden in seltsamen Schleifen gefangen.
Dies ist schwer zu vermeiden, da einige Semantiken Referenzschleifen oder Rundfunk implizieren und ziemlich chaotisch sein können, wenn das Netzwerk von Akteuren in einem unvorhersehbaren Zustand konvergiert (oder nicht).
In ähnlicher Weise können einige Zustände trotz genau definierter Kanten nicht erreicht werden, da der globale Zustand von der Lösung abweicht. 2 + 2 kann 4 werden oder nicht, abhängig davon, wann die 2 zu 2 wurden und ob sie so geblieben sind. Tabellenkalkulationen verfügen über synchrone Uhren und Schleifenerkennung. Verteilte Schauspieler tun dies im Allgemeinen nicht.
Alles viel Spaß :).
quelle
Ich habe dieses schöne Video auf dem Clojure-Subreddit über FRP gefunden. Es ist ziemlich leicht zu verstehen, auch wenn Sie Clojure nicht kennen.
Hier ist das Video: http://www.youtube.com/watch?v=nket0K1RXU4
Hier ist die Quelle, auf die sich das Video in der 2. Hälfte bezieht: https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs
quelle
Dieser Artikel von Andre Staltz ist die beste und klarste Erklärung, die ich bisher gesehen habe.
Einige Zitate aus dem Artikel:
Hier ist ein Beispiel für die fantastischen Diagramme, die Teil des Artikels sind:
quelle
Es geht um mathematische Datentransformationen über die Zeit (oder das Ignorieren der Zeit).
Im Code bedeutet dies funktionale Reinheit und deklarative Programmierung.
Staatliche Fehler sind ein großes Problem im Standard-Imperativ-Paradigma. Verschiedene Codebits können einen gemeinsamen Zustand zu unterschiedlichen "Zeiten" in der Programmausführung ändern. Das ist schwer zu bewältigen.
In FRP beschreiben Sie (wie in der deklarativen Programmierung), wie Daten von einem Zustand in einen anderen umgewandelt werden und was sie auslöst. Auf diese Weise können Sie die Zeit ignorieren, da Ihre Funktion einfach auf ihre Eingaben reagiert und ihre aktuellen Werte verwendet, um eine neue zu erstellen. Dies bedeutet, dass der Zustand im Diagramm (oder Baum) der Transformationsknoten enthalten ist und funktional rein ist.
Dies reduziert die Komplexität und die Debugging-Zeit massiv.
Denken Sie an den Unterschied zwischen A = B + C in Mathematik und A = B + C in einem Programm. In der Mathematik beschreiben Sie eine Beziehung, die sich nie ändern wird. In einem Programm heißt es, dass "Im Moment" A B + C ist. Der nächste Befehl könnte jedoch B ++ sein. In diesem Fall ist A nicht gleich B + C. In der mathematischen oder deklarativen Programmierung ist A immer gleich B + C, egal zu welchem Zeitpunkt Sie fragen.
Entfernen Sie also die Komplexität des gemeinsamen Zustands und ändern Sie die Werte im Laufe der Zeit. Ihr Programm ist viel einfacher zu überlegen.
Ein EventStream ist ein EventStream + eine Transformationsfunktion.
Ein Verhalten ist ein EventStream + Ein Wert im Speicher.
Wenn das Ereignis ausgelöst wird, wird der Wert durch Ausführen der Transformationsfunktion aktualisiert. Der Wert, den dies erzeugt, wird im Verhaltensspeicher gespeichert.
Verhaltensweisen können zusammengesetzt werden, um neue Verhaltensweisen zu erzeugen, die eine Transformation für N andere Verhaltensweisen darstellen. Dieser zusammengesetzte Wert wird neu berechnet, wenn die Eingabeereignisse (Verhaltensweisen) ausgelöst werden.
"Da Beobachter zustandslos sind, benötigen wir häufig mehrere von ihnen, um eine Zustandsmaschine wie im Drag-Beispiel zu simulieren. Wir müssen den Zustand speichern, in dem er für alle beteiligten Beobachter zugänglich ist, wie im obigen variablen Pfad."
Zitat aus - Verwerfen des Beobachtermusters http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf
quelle
Die kurze und klare Erklärung zur reaktiven Programmierung finden Sie in Cyclejs - Reaktive Programmierung . Sie verwendet einfache und visuelle Beispiele.
Es ist ein guter Startpunkt, keine vollständige Wissensquelle. Von dort aus konnte man zu komplexeren und tieferen Papieren springen.
quelle
Schauen Sie sich Rx, Reactive Extensions für .NET an. Sie weisen darauf hin, dass Sie mit IEnumerable im Grunde genommen aus einem Stream "ziehen". Linq-Abfragen über IQueryable / IEnumerable sind Set-Operationen, die die Ergebnisse aus einem Set "saugen". Mit denselben Operatoren über IObservable können Sie jedoch Linq-Abfragen schreiben, die "reagieren".
Sie können beispielsweise eine Linq-Abfrage wie (von m in MyObservableSetOfMouseMovements, wobei mX <100 und mY <100 einen neuen Punkt (mX, mY) auswählen) schreiben.
und mit den Rx-Erweiterungen ist es soweit: Sie haben UI-Code, der auf den eingehenden Strom von Mausbewegungen reagiert und zeichnet, wann immer Sie sich in der 100.100-Box befinden ...
quelle
FRP ist eine Kombination aus funktionaler Programmierung (Programmierparadigma, das auf der Idee basiert, dass alles eine Funktion ist) und reaktivem Programmierparadigma (basierend auf der Idee, dass alles ein Strom ist (Beobachter und beobachtbare Philosophie)). Es soll das Beste der Welt sein.
Schauen Sie sich zunächst den Beitrag von Andre Staltz zur reaktiven Programmierung an.
quelle