Qual è lo stato delle attuali implementazioni della programmazione reattiva funzionale?


90

Sto cercando di visualizzare alcuni semplici sistemi fisici automatici (cose come pendolo, bracci robotici, ecc.) In Haskell. Spesso questi sistemi possono essere descritti da equazioni come

df/dt = c*f(t) + u(t)

dove u(t)rappresenta una sorta di "controllo intelligente". Questi sistemi sembrano adattarsi molto bene al paradigma della programmazione reattiva funzionale.

Così ho preso il libro "The Haskell School of Expression" di Paul Hudak e ho scoperto che il linguaggio specifico del dominio "FAL" (per Functional Animation Language) qui presentato funziona in realtà abbastanza piacevolmente per i miei semplici sistemi giocattolo (sebbene alcune funzioni, in particolare integrate, sembrava essere un po 'troppo pigro per un uso efficiente, ma facilmente risolvibile).

La mia domanda è: qual è l'alternativa più matura, aggiornata, ben mantenuta e ottimizzata per le prestazioni per applicazioni più avanzate o anche pratiche oggi?

Questa pagina wiki elenca diverse opzioni per Haskell, ma non sono chiaro sui seguenti aspetti:

  1. Lo status di "reattivo", il progetto di Conal Eliott che è (a quanto ho capito) uno degli inventori di questo paradigma di programmazione, sembra un po 'stantio. Adoro il suo codice, ma forse dovrei provare altre alternative più aggiornate? Qual è la differenza principale tra loro, in termini di sintassi / prestazioni / stabilità del runtime?

  2. Per citare un sondaggio nel 2011, sezione 6, " ... le implementazioni FRP non sono ancora abbastanza efficienti o abbastanza prevedibili in termini di prestazioni per essere utilizzate efficacemente in domini che richiedono garanzie di latenza ... ". Anche se il sondaggio suggerisce alcune interessanti ottimizzazioni possibili, dato che FRP è lì da più di 15 anni, ho l'impressione che questo problema di prestazioni possa essere qualcosa di molto o addirittura intrinsecamente difficile da risolvere almeno entro pochi anni. È vero?

  3. Lo stesso autore dell'indagine parla di "perdite di tempo" nel suo blog . Il problema è unico per FRP o qualcosa che generalmente abbiamo quando programmiamo in un linguaggio puro e non rigoroso? Hai mai trovato semplicemente troppo difficile stabilizzare un sistema basato su FRP nel tempo, se non abbastanza performante?

  4. È ancora un progetto a livello di ricerca? Le persone come gli ingegneri di impianti, gli ingegneri di robotica, gli ingegneri finanziari, ecc. Li stanno effettivamente usando (in un linguaggio che si adatta alle loro esigenze)?

Anche se personalmente preferisco un'implementazione Haskell, sono aperto ad altri suggerimenti. Ad esempio, sarebbe particolarmente divertente avere un'implementazione Erlang --- sarebbe quindi molto facile avere un processo del server intelligente, adattivo e ad autoapprendimento!

Risposte:


82

In questo momento ci sono principalmente due pratiche librerie Haskell là fuori per la programmazione reattiva funzionale. Entrambi sono gestiti da singole persone, ma stanno ricevendo contributi in codice anche da altri programmatori Haskell:

  • Netwire si concentra su efficienza, flessibilità e prevedibilità. Ha un proprio paradigma di eventi e può essere utilizzato in aree in cui l'FRP tradizionale non funziona, inclusi i servizi di rete e le simulazioni complesse. Stile: applicativo e / o freccia. Autore e manutentore iniziale: Ertugrul Söylemez (questo sono io).

  • banana reattiva si basa sul paradigma FRP tradizionale. Sebbene sia pratico da usare, serve anche come base per la classica ricerca FRP. Il suo focus principale è sulle interfacce utente e c'è un'interfaccia già pronta per wx. Stile: applicativo. Autore e manutentore iniziale: Heinrich Apfelmus.

Dovresti provarli entrambi, ma a seconda dell'applicazione, probabilmente troverai uno o l'altro più adatto.

Per i giochi, il networking, il controllo dei robot e le simulazioni troverai utile Netwire. Viene fornito con cavi pronti per queste applicazioni, inclusi vari utili differenziali, integrali e molte funzionalità per la gestione trasparente degli eventi. Per un tutorial visita la documentazione del Control.Wiremodulo nella pagina che ho linkato.

Per le interfacce utente grafiche attualmente la scelta migliore è reattiva-banana. Ha già un'interfaccia wx (come una libreria separata reattiva-banana-wx) e Heinrich scrive molto su FRP in questo contesto, inclusi esempi di codice.

Per rispondere ad altre tue domande: FRP non è adatto in scenari in cui è necessaria la prevedibilità in tempo reale. Ciò è in gran parte dovuto a Haskell, ma sfortunatamente FRP è difficile da realizzare nei linguaggi di livello inferiore. Non appena Haskell stesso sarà pronto in tempo reale, anche FRP arriverà lì. Concettualmente Netwire è pronto per applicazioni in tempo reale.

Le perdite di tempo non sono più un problema, perché sono in gran parte legate alla struttura monadica. Le implementazioni pratiche di FRP semplicemente non offrono un'interfaccia monadica. Yampa ha iniziato questo e Netwire e reattivo-banana si basano entrambi su quello.

Al momento non sono a conoscenza di progetti commerciali o comunque su larga scala che utilizzano FRP. Le biblioteche sono pronte, ma penso che le persone non lo siano - ancora.


Ottima risposta, grazie .. sarà un esercizio divertente implementare alcuni algoritmi di apprendimento per rinforzo in cima alla tua libreria.
mnish

3
In particolare, un recente gioco indipendente scritto in Haskell ( Nikki and the Robots ) ha preso la decisione di non usare FRP.
Alex R

23

Sebbene ci siano già alcune buone risposte, cercherò di rispondere alle tue domande specifiche.

  1. reattivo non è utilizzabile per progetti seri, a causa di problemi di perdita di tempo. (vedi # 3). L'attuale libreria con il design più simile è reattivo-banana, che è stato sviluppato con reattivo come ispirazione e in discussione con Conal Elliott.

  2. Sebbene Haskell stesso non sia appropriato per applicazioni hard real-time, in alcuni casi è possibile utilizzare Haskell per applicazioni soft realtime. Non ho familiarità con la ricerca attuale, ma non credo che questo sia un problema insormontabile. Sospetto che sistemi come Yampa o sistemi di generazione di codice come Atom siano forse l'approccio migliore per risolvere questo problema.

  3. Una "perdita di tempo" è un problema specifico per FRP commutabile. La perdita si verifica quando un sistema non è in grado di liberare vecchi oggetti perché potrebbe averne bisogno se in futuro dovesse verificarsi un cambio. Oltre a una perdita di memoria (che può essere piuttosto grave), un'altra conseguenza è che, quando si verifica lo switch, il sistema deve fermarsi mentre la catena di vecchi oggetti viene attraversata per generare lo stato corrente.

Le librerie frp non commutabili come Yampa e le versioni precedenti di reattive-banana non soffrono di perdite di tempo. Le librerie frp commutabili generalmente impiegano uno di due schemi: o hanno una speciale "monade di creazione" in cui vengono creati i valori FRP, oppure utilizzano un parametro di tipo "invecchiamento" per limitare i contesti in cui possono verificarsi gli switch. elerea (e forse netwire?) usa il primo, mentre i recenti reattivi-banana e pompelmo usano il secondo.

Per "frp commutabile", intendo uno che implementa la funzione di Conal switcher :: Behavior a -> Event (Behavior a) -> Behavior a, o semantica identica. Ciò significa che la forma della rete può cambiare dinamicamente mentre viene eseguita.

Questo in realtà non contraddice l'affermazione di @ ertes sulle interfacce monadiche: risulta che fornire Monadun'istanza per un fileEvent rende possibili perdite di tempo, e con nessuno degli approcci precedenti non è più possibile definire le istanze di Monad equivalenti.

Infine, sebbene ci sia ancora molto lavoro da fare con FRP, penso che alcune delle piattaforme più recenti (reattive-banana, elerea, netwire) siano stabili e abbastanza mature da poter creare codice affidabile da esse. Ma potrebbe essere necessario dedicare molto tempo all'apprendimento dei dettagli per capire come ottenere una buona prestazione.


2
Per quanto riguarda le librerie basate su frecce (Yampa, netwire), sono anch'esse commutabili. Il motivo è che le frecce hanno l'invecchiamento integrato, non puoi effettivamente sbarazzartene. (Essendo trasformatori di flusso, le frecce sono agnostiche sull'ora di inizio del loro flusso di input.)
Heinrich Apfelmus


1
@ HeinrichApfelmus: questo è un punto interessante. Generalmente non penso che le librerie basate su frecce siano commutabili nello stesso modo in cui lo sono elerea / pompelmo / current-reattivo-banana. Penso che il loro passaggio sia molto più vicino a quanto richiesto nelle versioni precedenti di reattivo-banana. Questa è solo una sensazione viscerale, però, non ci ho pensato abbastanza per descrivere cosa intendo.
John L

2
@ DanBurton grazie, stavo tentando senza successo di ricordare quel nome. Sono d'accordo che il sodio dovrebbe essere considerato una moderna libreria FRP, anche se non è così popolare come la banana reattiva.
John L

Sebbene la discussione in corso sia un po 'difficile da seguire, sembra indicare che un sistema soft-realtime è effettivamente possibile, a condizione che il tempo GC possa in qualche modo essere limitato. Comunque grazie per la tua ottima risposta.
mnish

20

Elencherò un paio di elementi nello spazio Mono e .Net e uno nello spazio Haskell che ho trovato non molto tempo fa. Inizierò con Haskell.

Olmo - collegamento

La sua descrizione secondo il suo sito:

Elm mira a rendere più piacevole lo sviluppo web front-end. Introduce un nuovo approccio alla programmazione GUI che corregge i problemi sistemici di HTML, CSS e JavaScript. Elm ti consente di lavorare rapidamente e facilmente con il layout visivo, utilizzare l'area di disegno, gestire l'input utente complicato e fuggire dall'inferno di richiamata.

Ha la sua variante di FRP . Giocando con i suoi esempi sembra piuttosto maturo.

Estensioni reattive - collegamento

Descrizione dalla sua prima pagina:

Reactive Extensions (Rx) è una libreria per la composizione di programmi asincroni e basati su eventi utilizzando sequenze osservabili e operatori di query in stile LINQ. Utilizzando Rx, gli sviluppatori rappresentano flussi di dati asincroni con Observables, eseguono query su flussi di dati asincroni utilizzando operatori LINQ e parametrizzano la concorrenza nei flussi di dati asincroni utilizzando Scheduler. In poche parole, Rx = Observables + LINQ + Scheduler.

Reactive Extensions proviene da MSFT e implementa molti operatori eccellenti che semplificano la gestione degli eventi. È stato reso disponibile solo un paio di giorni fa. È molto maturo e utilizzato nella produzione; a mio parere sarebbe stata un'API migliore per le API di Windows 8 rispetto a quella fornita dalla libreria TPL; perché le osservabili possono essere sia calde che fredde e riprovate / unite ecc., mentre le attività rappresentano sempre calcoli caldi o eseguiti che sono in esecuzione, in errore o completati.

Ho scritto codice lato server usando Rx per asincronicità, ma devo ammettere che scrivere funzionalmente in C # può essere un po 'fastidioso. F # ha un paio di wrapper, ma è stato difficile tenere traccia dello sviluppo dell'API, perché il gruppo è relativamente chiuso e non è promosso da MSFT come lo sono altri progetti.

Il suo open source è arrivato con l'open source del suo compilatore IL-to-JS, quindi potrebbe probabilmente funzionare bene con JavaScript o Elm.

Probabilmente potresti associare F # / C # / JS / Haskell insieme molto bene usando un broker di messaggi, come RabbitMQ e SocksJS.

Toolkit dell'interfaccia utente di Bling - collegamento

Descrizione dalla sua prima pagina:

Bling è una libreria basata su C # per programmare facilmente immagini, animazioni, interazioni e visualizzazioni su WPF / .NET di Microsoft. Bling è orientato verso i tecnologi del design, cioè i designer che a volte programmano, per aiutare nella prototipazione rapida di idee di progettazione dell'interfaccia utente avanzate. Studenti, artisti, ricercatori e hobbisti troveranno inoltre Bling utile come strumento per esprimere rapidamente idee o visualizzazioni. Le API e i costrutti di Bling sono ottimizzati per la programmazione rapida del codice usa e getta invece che per un'attenta programmazione del codice di produzione.

Articolo LtU in omaggio .

L'ho testato, ma non ci ho lavorato per un progetto cliente. Sembra fantastico, ha un bel sovraccarico di operatori C # che forma le associazioni tra i valori. Utilizza le proprietà di dipendenza in WPF / SL / (WinRT) come origini evento. Le sue animazioni 3D funzionano bene su hardware ragionevole. Lo userei se finissi su un progetto che necessita di visualizzazioni; probabilmente portandolo su Windows 8.

ReactiveUI - collegamento

Paul Betts, precedentemente a MSFT, ora a Github, ha scritto quel framework. Ci ho lavorato abbastanza ampiamente e mi piace il modello. È più disaccoppiato di Blink (per sua natura dall'uso di Rx e delle sue astrazioni), rendendo più semplice il codice di unit test che lo utilizza. Il client gitHub git per Windows è scritto in questo.

Commenti

Il modello reattivo è sufficientemente performante per la maggior parte delle applicazioni che richiedono prestazioni. Se stai pensando al difficile in tempo reale, scommetterei che la maggior parte dei linguaggi GC ha problemi. Rx, ReactiveUI crea una certa quantità di piccoli oggetti che devono essere sottoposti a GC, perché è così che le sottoscrizioni vengono create / eliminate e i valori intermedi vengono fatti avanzare nella "monade" reattiva dei callback. In generale su .Net preferisco la programmazione reattiva rispetto alla programmazione basata su attività perché i callback sono statici (noti in fase di compilazione, nessuna allocazione) mentre le attività sono allocate dinamicamente (non noto, tutte le chiamate richiedono un'istanza, garbage creato) - e lambda si compilano in classi generate dal compilatore.

Ovviamente C # e F # sono valutati rigorosamente, quindi la perdita di tempo non è un problema qui. Lo stesso per JS. Tuttavia, può essere un problema con le osservabili riproducibili o memorizzate nella cache.


Grazie per la magnifica risposta. Una delle cose che mi sono piaciute per le implementazioni FRP Haskell è che sembrano permettermi di disaccoppiare in modo pulito i calcoli per il controllo u(t)e le simulazioni per f(t). È questo il caso delle implementazioni di F #?
mnish

Immagino si possa dire che queste due funzioni sono temporalmente disaccoppiate, sì. Tuttavia, probabilmente non sono disaccoppiati logicamente. ;)
Henrik

Per quanto ne so, Reactive Extensions e gli altri pacchetti focalizzati sull'interfaccia utente più raffinati (e tutto al di fuori di Haskell, in effetti) usano solo semanica con eventi, vale a dire che hanno una nozione di eventi che puoi attivare, ma non una nozione di segnali temporali continui che possono interagire equazionalmente. Per la costruzione di guis va bene, credo. Tuttavia, per la creazione di simulazioni e modelli, ciò potrebbe essere sfortunato.
sclv

Stai insinuando che tutte le implementazioni di lib di programmazione reattiva funzionale necessitano di modellare il tempo in modo continuo piuttosto che discreto? Ho trovato un articolo intitolato "Process Algebra with Timing: Real Time and Discrete Time": è un buon punto di partenza per capire di cosa stai parlando?
Henrik

Non sto dicendo che tutti debbano - alcuni lo fanno, altri no. Ma quelli che lo fanno sono più adatti per determinati compiti e quelli che non lo fanno sono più adatti per altri ...
sclv
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.