Domanda

Ho letto l'articolo di Wikipedia su programmazione reattiva.Ho letto anche il piccolo articolo su programmazione reattiva funzionale.Le descrizioni sono piuttosto astratte.

  1. Cosa significa in pratica la programmazione reattiva funzionale (FRP)?
  2. In cosa consiste la programmazione reattiva (al contrario della programmazione non reattiva?)?

Il mio background è nei linguaggi imperativi/OO, quindi sarebbe apprezzata una spiegazione relativa a questo paradigma.

È stato utile?

Soluzione

Se vuoi avere un'idea di FRP, potresti iniziare con il vecchio Tutorial di Fran dal 1998, che ha illustrazioni animate. Per i documenti, inizia con Animazione reattiva funzionale e segui i link su il link delle pubblicazioni sulla mia home page e il FRP sul link Haskell wiki .

Personalmente, mi piace pensare al significato di FRP prima di esaminare come potrebbe essere implementato. (Il codice senza una specifica è una risposta senza una domanda e quindi & Quot; nemmeno sbagliato & Quot ;.) Quindi non descrivo FRP in termini di rappresentazione / implementazione come Thomas K fa in un'altra risposta (grafici, nodi, spigoli, spari, esecuzione, ecc.). Esistono molti stili di implementazione possibili, ma nessuna implementazione dice cosa sia FRem .

Mi associo alla semplice descrizione di Laurence G secondo cui FRP è circa " tipi di dati che rappresentano un valore "nel tempo" " ;. La programmazione imperativa convenzionale cattura questi valori dinamici solo indirettamente, attraverso lo stato e le mutazioni. La storia completa (passato, presente, futuro) non ha una rappresentazione di prima classe. Inoltre, solo i valori in evoluzione discreta possono essere catturati (indirettamente), poiché il paradigma imperativo è temporalmente discreto. Al contrario, FRP acquisisce questi valori in evoluzione direttamente e non ha difficoltà con continuamente valori in evoluzione.

FRP è anche insolito in quanto è concorrente senza correre l'amplificatore & teorico; nido di ratti pragmatici che affligge la concorrenza imperativa. Semanticamente, la concorrenza di FRP è a grana fine , determinata e continua . (Sto parlando di significato, non di implementazione. Un'implementazione può o meno implicare concorrenza o parallelismo.) La determinazione semantica è molto importante per il ragionamento, sia rigoroso che informale. Mentre la concorrenza aggiunge un'enorme complessità alla programmazione imperativa (a causa dell'interlacciamento non deterministico), è semplice in FRP.

Allora, cos'è l'FRP? Avresti potuto inventarlo da solo. Inizia con queste idee:

  • I valori dinamici / in evoluzione (ovvero i valori " nel tempo ") sono valori di prima classe in se stessi. Puoi definirli e combinarli, passarli in & Amp; fuori funzione. Ho chiamato queste cose "behaviors".

  • I comportamenti sono costituiti da alcune primitive, come comportamenti (statici) costanti e tempo (come un orologio), e quindi con una combinazione sequenziale e parallela. I comportamenti n sono combinati applicando una funzione n-ary (sui valori statici), " point-wise " ;, cioè, continuamente nel tempo.

  • Per tenere conto di fenomeni discreti, avere un altro tipo (famiglia) di " eventi " ;, ognuno dei quali ha un flusso (finito o infinito) di occorrenze. Ogni ricorrenza ha un tempo e un valore associati.

  • Per inventare il vocabolario compositivo dal quale possono essere costruiti tutti i comportamenti e gli eventi, gioca con alcuni esempi. Continua a decostruire in pezzi più generali / semplici.

  • In modo che tu sappia di essere su un terreno solido, dai a tutto il modello una base compositiva, usando la tecnica della semantica denotazionale, il che significa solo che (a) ogni tipo ha un corrispondente semplice &; preciso tipo matematico di " significati " ;, e (b) ogni primitiva e operatore ha un semplice & amp; significato preciso in funzione dei significati dei componenti. Mai, mai mescolare considerazioni di implementazione nel processo di esplorazione. Se questa descrizione è per te incomprensibile, consulta (a) Design denotazionale con morfismi di classe di tipo , (b) Programmazione reattiva funzionale Push-pull (ignorando i bit di implementazione) e (c) il Semantica denotazionale pagina dei wikibooks di Haskell . Attenzione che la semantica denotazionale ha due parti, dai suoi due fondatori Christopher Strachey e Dana Scott: l'amplificatore & Più semplice; la parte Strachey più utile e la parte Scott più difficile e meno utile (per la progettazione di software).

Se segui questi principi, mi aspetto che otterrai qualcosa più o meno nello spirito di FRP.

Dove ho preso questi principi? Nella progettazione del software, faccio sempre la stessa domanda: & Quot; cosa significa? & Quot ;. La semantica denotazionale mi ha dato un quadro preciso per questa domanda, e una che si adatta alla mia estetica (diversamente dalla semantica operativa o assiomatica, che mi lasciano insoddisfatto). Quindi mi sono chiesto che cos'è il comportamento? Presto mi sono reso conto che la natura temporalmente discreta del calcolo imperativo è una sistemazione per un particolare stile di macchina , piuttosto che una descrizione naturale del comportamento stesso. La descrizione più semplice e precisa del comportamento che mi viene in mente è semplicemente & Quot; funzione del (continuo) tempo & Quot ;, quindi questo è il mio modello. Deliziosamente, questo modello gestisce la concorrenza continua e deterministica con facilità e grazia.

È stata una vera sfida implementare questo modello in modo corretto ed efficiente, ma questa è un'altra storia.

Altri suggerimenti

Nella pura programmazione funzionale, non ci sono effetti collaterali. Per molti tipi di software (ad esempio qualsiasi cosa con l'interazione dell'utente) sono necessari effetti collaterali a un certo livello.

Un modo per ottenere un comportamento simile agli effetti collaterali pur mantenendo uno stile funzionale è usare la programmazione reattiva funzionale. Questa è la combinazione di programmazione funzionale e programmazione reattiva. (L'articolo di Wikipedia a cui ti sei collegato riguarda l'ultimo.)

L'idea alla base della programmazione reattiva è che ci sono alcuni tipi di dati che rappresentano un valore " nel tempo " ;. I calcoli che coinvolgono questi valori che cambiano nel tempo avranno essi stessi valori che cambiano nel tempo.

Ad esempio, è possibile rappresentare le coordinate del mouse come una coppia di valori interi nel tempo. Diciamo che abbiamo avuto qualcosa di simile (questo è pseudo-codice):

x = <mouse-x>;
y = <mouse-y>;

In qualsiasi momento, xey avrebbe le coordinate del mouse. A differenza della programmazione non reattiva, dobbiamo eseguire questa assegnazione una sola volta, e le variabili xey rimarranno & Quot; aggiornate & Quot; automaticamente. Questo è il motivo per cui la programmazione reattiva e la programmazione funzionale funzionano così bene insieme: la programmazione reattiva elimina la necessità di mutare le variabili pur consentendo di fare molto di ciò che si potrebbe ottenere con le mutazioni variabili.

Se poi eseguiamo alcuni calcoli basati su questo, anche i valori risultanti saranno valori che cambiano nel tempo. Ad esempio:

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

In questo esempio, minX sarà sempre 16 in meno della coordinata x del puntatore del mouse. Con le librerie sensibili alla reattività potresti quindi dire qualcosa del tipo:

rectangle(minX, minY, maxX, maxY)

E un riquadro 32x32 verrà disegnato attorno al puntatore del mouse e lo seguirà ovunque si sposti.

Ecco un abbastanza efficace programmazione reattiva .

Un modo semplice per arrivare a una prima intuizione su come è immaginare che il tuo programma sia un foglio di calcolo e tutte le tue variabili siano celle. Se una qualsiasi delle celle in un foglio di calcolo cambia, anche le celle che fanno riferimento a quella cella cambiano. È lo stesso con FRP. Ora immagina che alcune celle cambino da sole (o meglio, siano prese dal mondo esterno): in una situazione della GUI, la posizione del mouse sarebbe un buon esempio.

Che necessariamente manca molto. La metafora si interrompe abbastanza rapidamente quando si utilizza effettivamente un sistema FRP. Per uno, di solito ci sono tentativi di modellare anche eventi discreti (ad esempio il mouse su cui si fa clic). Lo sto solo mettendo qui per darti un'idea di come sia.

Per me si tratta di 2 significati diversi del simbolo =:

  1. In matematica x = sin(t) significa che x è nome diverso per sin(t). Quindi scrivere x + y è la stessa cosa di sin(t) + y. La programmazione reattiva funzionale è come la matematica in questo senso: se scrivi t, viene calcolata con qualunque sia il valore di <=> nel momento in cui viene usata.
  2. In linguaggi di programmazione simil-C (linguaggi imperativi), <=> è un compito: significa che <=> memorizza il valore di <=> preso al momento del compito.

OK, dalla conoscenza di base e dalla lettura della pagina Wikipedia a cui hai indicato, sembra che la programmazione reattiva sia qualcosa di simile al calcolo del flusso di dati ma con specifici "stimoli" esterni che attivano una serie di nodi per attivarsi ed eseguire i propri calcoli.

Questo è molto adatto alla progettazione dell'interfaccia utente, ad esempio, in cui toccando un controllo dell'interfaccia utente (ad esempio, il controllo del volume su un'applicazione di riproduzione musicale) potrebbe essere necessario aggiornare vari elementi di visualizzazione e il volume effettivo dell'output audio.Quando si modifica il volume (uno slider, diciamo) ciò corrisponderebbe alla modifica del valore associato a un nodo in un grafico diretto.

Vari nodi con bordi da quel nodo "valore volume" verrebbero automaticamente attivati ​​e tutti i calcoli e gli aggiornamenti necessari si diffonderebbero naturalmente attraverso l'applicazione.L'applicazione "reagisce" allo stimolo dell'utente.La programmazione reattiva funzionale sarebbe semplicemente l'implementazione di questa idea in un linguaggio funzionale, o generalmente all'interno di un paradigma di programmazione funzionale.

Per ulteriori informazioni sul "dataflow computing", cerca queste due parole su Wikipedia o utilizzando il tuo motore di ricerca preferito.L'idea generale è questa:il programma è un grafo diretto di nodi, ciascuno dei quali esegue alcuni semplici calcoli.Questi nodi sono collegati tra loro tramite collegamenti grafici che forniscono gli output di alcuni nodi agli input di altri.

Quando un nodo si attiva o esegue il suo calcolo, i nodi collegati alle sue uscite hanno i loro ingressi corrispondenti "attivati" o "contrassegnati".Qualsiasi nodo che abbia tutti gli input attivati/contrassegnati/disponibili si attiva automaticamente.Il grafico potrebbe essere implicito o esplicito a seconda di come viene implementata la programmazione reattiva.

I nodi possono essere considerati come attivati ​​in parallelo, ma spesso vengono eseguiti in serie o con parallelismo limitato (ad esempio, potrebbero esserci alcuni thread che li eseguono).Un esempio famoso è stato il Macchina per il flusso di dati di Manchester, che (IIRC) utilizzava un'architettura di dati con tag per pianificare l'esecuzione dei nodi nel grafico attraverso una o più unità di esecuzione.Il dataflow computing è abbastanza adatto a situazioni in cui l'attivazione di calcoli in modo asincrono che danno origine a cascate di calcoli funziona meglio che cercare di far sì che l'esecuzione sia governata da uno o più orologi.

La programmazione reattiva importa questa idea di "cascata di esecuzione" e sembra pensare al programma in modo simile a un flusso di dati, ma con la clausola che alcuni nodi siano agganciati al "mondo esterno" e le cascate di esecuzione vengano attivate quando questi nodi sensoriali i nodi simili cambiano.L'esecuzione del programma assomiglierebbe quindi a qualcosa di analogo a un arco riflesso complesso.Il programma può essere o meno fondamentalmente sessile tra gli stimoli o può stabilizzarsi in uno stato fondamentalmente sessile tra gli stimoli.

La programmazione "non reattiva" sarebbe una programmazione con una visione molto diversa del flusso di esecuzione e della relazione con gli input esterni.È probabile che sia in qualche modo soggettivo, dal momento che le persone saranno probabilmente tentate di dire che tutto ciò che risponde a input esterni "reagisce" ad essi.Ma guardando lo spirito della cosa, un programma che interroga una coda di eventi a un intervallo fisso e invia tutti gli eventi trovati alle funzioni (o ai thread) è meno reattivo (perché si occupa solo dell'input dell'utente a un intervallo fisso).Ancora una volta, è lo spirito della cosa qui:si può immaginare di inserire un'implementazione di polling con un intervallo di polling veloce in un sistema a un livello molto basso e programmarla in modo reattivo sopra di essa.

Dopo aver letto molte pagine su FRP, ho finalmente trovato questo testi illuminanti su FRP, finalmente mi hanno fatto capire di cosa si tratta veramente.

Cito di seguito Heinrich Apfelmus (autore di banana reattiva).

  

Qual è l'essenza della programmazione reattiva funzionale?

     

Una risposta comune sarebbe che & # 8220; FRP riguarda la descrizione di un sistema in   termini di funzioni variabili nel tempo anziché stato mutabile & # 8221 ;, e quello   non sarebbe certamente sbagliato. Questo è il punto di vista semantico. Ma in   la mia opinione, la risposta più profonda e soddisfacente è data dal   seguendo il criterio puramente sintattico:

     

L'essenza della programmazione reattiva funzionale è di specificare completamente il comportamento dinamico di un valore al momento della dichiarazione.

     

Ad esempio, prendi l'esempio di un contatore: hai due pulsanti   etichettato & # 8220; Su & # 8221; e & # 8220; Giù & # 8221; che può essere utilizzato per aumentare o diminuire   il contatore. Imperativamente, dovresti prima specificare un valore iniziale   e poi cambiarlo ogni volta che viene premuto un pulsante; qualcosa del genere:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)
     

Il punto è che al momento della dichiarazione, solo il valore iniziale   per il contatore è specificato; il comportamento dinamico del contatore è   implicito nel resto del testo del programma. Al contrario, funzionale   la programmazione reattiva specifica l'intero comportamento dinamico al momento   di dichiarazione, in questo modo:

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)
     

Ogni volta che vuoi capire le dinamiche del contatore, hai solo   per vedere la sua definizione. Tutto ciò che può accadere lo farà   appare sul lato destro. Questo è molto in contrasto con il   approccio imperativo in cui le successive dichiarazioni possono cambiare il   comportamento dinamico di valori precedentemente dichiarati.

Quindi, nella mia comprensione un programma FRP è un insieme di equazioni: inserisci qui la descrizione dell'immagine

j è discreto: 1,2,3,4 ...

f dipende da t, quindi è possibile modellare gli stimoli esterni

tutto lo stato del programma è incapsulato nelle variabili x_i

La libreria FRP si occupa del tempo di avanzamento, in altre parole, passando da j+1 a x_i'.

Spiego queste equazioni in modo molto più dettagliato in questo video.

Modifica

Circa 2 anni dopo la risposta originale, recentemente sono giunto alla conclusione che le implementazioni FRP hanno un altro aspetto importante. Devono (e di solito lo fanno) risolvere un importante problema pratico: invalidamento della cache .

Le equazioni per f=g.map(_+1) - s descrivono un grafico delle dipendenze. Quando alcune g cambiano alla volta List, non tutti gli altri Ints valori in x_i(t_j) devono essere aggiornati, quindi non tutte le dipendenze devono essere ricalcolate perché alcune x_j(t_j) potrebbero essere indipendenti da < =>.

Inoltre, map - le s che cambiano possono essere aggiornate in modo incrementale. Ad esempio, consideriamo un'operazione sulla mappa f_i in Scala, dove <=> e <=> sono <=> di <=>. Qui <=> corrisponde a <=> e <=> è <=>. Ora, se antepongo un elemento a <=>, sarebbe inutile eseguire l'operazione <=> per tutti gli elementi in <=>. Alcune implementazioni FRP (ad esempio calcolo incrementale.

In altre parole, i comportamenti (i <=> - s) in FRP possono essere pensati come calcoli memorizzati nella cache. È compito del motore FRP invalidare e ricalcolare in modo efficiente questi cache-s (i <=> - s) se alcuni dei <=> - s cambiano.

L'articolo Reattività funzionale semplicemente efficiente di Conal Elliott ( PDF diretto , 233 & nbsp; KB) è una buona introduzione. Funziona anche la libreria corrispondente.

Il documento è ora sostituito da un altro documento, Programmazione reattiva funzionale push-pull ( PDF diretto , 286 nbsp &;. KB)

Dichiarazione di non responsabilità: la mia risposta è nel contesto di rx.js - una libreria di "programmazione reattiva" per Javascript.

Nella programmazione funzionale, anziché scorrere ogni elemento di una raccolta, si applicano funzioni di ordine superiore (HoF) alla raccolta stessa. Quindi l'idea alla base di FRP è che invece di elaborare ogni singolo evento, creare un flusso di eventi (implementato con un osservabile *) e applicare invece HoF a quello. In questo modo è possibile visualizzare il sistema come pipeline di dati che collegano gli editori agli abbonati.

I principali vantaggi dell'utilizzo di un osservabile sono:
i) estrae lo stato dal codice, ad esempio, se si desidera che il gestore eventi venga licenziato solo per ogni 'n' evento, o smetta di sparare dopo i primi eventi 'n, o inizi a sparare solo dopo il primo' n 'eventi, puoi semplicemente usare gli HoF (filter, takeUntil, skip rispettivamente) invece di impostare, aggiornare e controllare i contatori.
ii) migliora la localizzazione del codice - se hai 5 gestori di eventi diversi che cambiano lo stato di un componente, puoi unire i loro osservabili e definire un singolo gestore di eventi sull'osservabile unito invece, combinando efficacemente 5 gestori di eventi in 1. Questo lo rende molto facile ragionare su quali eventi nell'intero sistema possono influenzare un componente, poiché è tutto presente in un singolo gestore.

  • Un osservabile è il doppio di un Iterabile.

Un Iterable è una sequenza consumata pigramente: ogni elemento viene estratto dall'iteratore ogni volta che lo desidera, e quindi l'enumerazione è guidata dal consumatore.

Un osservabile è una sequenza prodotta pigramente - ogni elemento viene spinto all'osservatore ogni volta che viene aggiunto alla sequenza, e quindi l'enumerazione è guidata dal produttore.

Amico, questa è un'idea strabiliante e geniale! Perché non l'ho scoperto nel 1998? Comunque, ecco la mia interpretazione del tutorial Fran . I suggerimenti sono i benvenuti, sto pensando di avviare un motore di gioco basato su questo.

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

In breve: se ogni componente può essere trattato come un numero, l'intero sistema può essere trattato come un'equazione matematica, giusto?

Il libro di Paul Hudak, The Haskell School of Expression , non è solo una bella introduzione a Haskell, ma spende anche un bel po 'di tempo su FRP. Se sei un principiante con FRP, lo consiglio vivamente di darti un'idea di come funziona FRP.

C'è anche quello che sembra una nuova riscrittura di questo libro (pubblicato nel 2011, aggiornato 2014), The Haskell School of Music .

Secondo le risposte precedenti, sembra che matematicamente, pensiamo semplicemente in un ordine superiore. Invece di pensare a un valore x di tipo X , pensiamo a una funzione x : T & # 8594; X , dove T è il tipo di tempo, siano essi numeri naturali, numeri interi o continuum. Ora quando scriviamo y : = x + 1 nel linguaggio di programmazione, intendiamo effettivamente l'equazione y ( t ) = x ( t ) + 1.

Funziona come un foglio di calcolo come indicato. Generalmente basato su un framework guidato da eventi.

Come per tutti i " paradigmi " ;, la sua novità è discutibile.

Dalla mia esperienza di reti di attori a flusso distribuito, può facilmente cadere in preda a un problema generale di coerenza statale attraverso la rete di nodi, vale a dire che si finisce con molta oscillazione e intrappolamento in strani circuiti.

Questo è difficile da evitare poiché alcune semantiche implicano loop o trasmissioni referenziali e possono essere piuttosto caotiche poiché la rete di attori converge (o no) in uno stato imprevedibile.

Allo stesso modo, alcuni stati potrebbero non essere raggiunti, nonostante abbiano contorni ben definiti, perché lo stato globale si allontana dalla soluzione. 2 + 2 possono o meno essere 4 a seconda di quando i 2 sono diventati 2 e se sono rimasti in quel modo. I fogli di calcolo dispongono di clock e rilevamento loop sincroni. Gli attori distribuiti generalmente no.

Tutto molto divertente :).

Ho trovato questo bel video sul subreddit Clojure su FRP. È abbastanza facile da capire anche se non conosci Clojure.

Ecco il video: http://www.youtube.com/watch?v=nket0K1RXU4

Ecco la fonte a cui fa riferimento il video nella seconda metà: https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs

Questo articolo di Andre Staltz è la spiegazione migliore e più chiara che abbia mai visto finora.

Alcune citazioni dall'articolo:

  

La programmazione reattiva sta programmando con flussi di dati asincroni.

     

Inoltre, ti viene data una straordinaria serie di funzioni per combinare, creare e filtrare uno di questi flussi.

Ecco un esempio dei fantastici diagrammi che fanno parte dell'articolo:

 Fai clic sul diagramma del flusso di eventi

Riguarda le trasformazioni matematiche dei dati nel tempo (o ignorando il tempo).

Nel codice questo significa purezza funzionale e programmazione dichiarativa.

I bug di stato sono un grosso problema nel paradigma imperativo standard. Vari bit di codice possono cambiare uno stato condiviso in & Quot; volte & Quot; nell'esecuzione dei programmi. Questo è difficile da affrontare.

In FRP descrivi (come nella programmazione dichiarativa) come i dati si trasformano da uno stato all'altro e cosa li innesca. Questo ti permette di ignorare il tempo perché la tua funzione sta semplicemente reagendo ai suoi input e usando i loro valori attuali per crearne uno nuovo. Ciò significa che lo stato è contenuto nel grafico (o nella struttura) dei nodi di trasformazione ed è funzionalmente puro.

Ciò riduce enormemente la complessità e i tempi di debug.

Pensa alla differenza tra A = B + C in matematica e A = B + C in un programma. In matematica stai descrivendo una relazione che non cambierà mai. In un programma, si dice che & Quot; In questo momento & Quot; A è B + C. Ma il prossimo comando potrebbe essere B ++, nel qual caso A non è uguale a B + C. Nella programmazione matematica o dichiarativa A sarà sempre uguale a B + C, indipendentemente dal momento in cui lo chiedi.

Quindi rimuovendo le complessità dello stato condiviso e modificando i valori nel tempo. È molto più facile ragionare sul programma.

Un EventStream è un EventStream + alcune funzioni di trasformazione.

Un comportamento è un EventStream + Qualche valore in memoria.

Quando viene generato l'evento, il valore viene aggiornato eseguendo la funzione di trasformazione. Il valore che questo produce è memorizzato nella memoria del comportamento.

I comportamenti possono essere composti per produrre nuovi comportamenti che sono una trasformazione su N altri comportamenti. Questo valore composto verrà ricalcolato quando gli eventi di input (comportamenti) vengono attivati.

" Dato che gli osservatori sono apolidi, ne abbiamo spesso bisogno per simulare una macchina a stati come nell'esempio di trascinamento. Dobbiamo salvare lo stato in cui è accessibile a tutti gli osservatori coinvolti, come nel percorso della variabile sopra. & Quot;

Citazione da - Deprecating The Observer Pattern http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010. pdf

La spiegazione breve e chiara sulla Programmazione Reattiva appare su Cyclejs - Programmazione reattiva, utilizza campioni semplici e visivi.

A [modulo/Componente/oggetto] è reattivo significa che è pienamente responsabile della gestione del proprio stato reagendo agli eventi esterni.

Qual è il vantaggio di questo approccio?È Inversione di controllo, principalmente perché [modulo/componente/oggetto] è responsabile di se stesso, migliorando l'incapsulamento usando metodi privati ​​contro quelli pubblici.

È un buon punto di partenza, non una fonte completa di conoscenza.Da lì potresti passare a documenti più complessi e profondi.

Dai un'occhiata a Rx, Extctive Reactive per .NET. Sottolineano che con IEnumerable stai praticamente "estraendo" da un flusso. Le query Linq su IQueryable / IEnumerable sono operazioni di set che "aspirano" i risultati da un set. Ma con gli stessi operatori su IObservable puoi scrivere query Linq che "reagiscono".

Ad esempio, potresti scrivere una query Linq come (da m in MyObservableSetOfMouseMovements dove m.X < 100 e m.Y < 100 seleziona nuovo punto (m.X, m.Y)).

e con le estensioni Rx, questo è tutto: hai un codice UI che reagisce al flusso in entrata dei movimenti del mouse e disegna ogni volta che sei nella casella 100,100 ...

FRP è una combinazione di programmazione funzionale (paradigma di programmazione basato sull'idea di tutto è una funzione) e paradigma di programmazione reattiva (basato sull'idea che tutto è un flusso (osservatore e filosofia osservabile)). Dovrebbe essere il migliore dei mondi.

Dai un'occhiata al post di Andre Staltz sulla programmazione reattiva per iniziare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top