¿Cuál es el estado de las implementaciones actuales de programación reactiva funcional?

90

Estoy tratando de visualizar algunos sistemas físicos automáticos simples (cosas como péndulos, brazos robóticos, etc.) en Haskell. A menudo, esos sistemas pueden describirse mediante ecuaciones como

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

donde u(t)representa algún tipo de "control inteligente". Esos sistemas parecen encajar muy bien en el paradigma de programación reactiva funcional.

Así que tomé el libro "The Haskell School of Expression" de Paul Hudak y descubrí que el lenguaje específico del dominio "FAL" (para Functional Animation Language) que se presenta allí en realidad funciona bastante bien para mis sistemas de juguetes simples (aunque algunas funciones, en particular integrate, parecía ser un poco perezoso para un uso eficiente, pero fácilmente reparable).

Mi pregunta es, ¿cuál es la alternativa más madura, actualizada, bien mantenida y ajustada al rendimiento para aplicaciones más avanzadas o incluso prácticas en la actualidad?

Esta página wiki enumera varias opciones para Haskell, pero no tengo claros los siguientes aspectos:

  1. El estado de "reactivo", el proyecto de Conal Eliott que es (según tengo entendido) uno de los inventores de este paradigma de programación, parece un poco obsoleto. Me encanta su código, pero tal vez debería probar otras alternativas más actualizadas. ¿Cuál es la principal diferencia entre ellos, en términos de sintaxis / rendimiento / estabilidad en tiempo de ejecución?

  2. Para citar una encuesta en 2011, Sección 6, " ... las implementaciones de FRP todavía no son lo suficientemente eficientes o predecibles en el rendimiento para ser utilizadas de manera efectiva en dominios que requieren garantías de latencia ... ". Aunque la encuesta sugiere algunas optimizaciones posibles interesantes, dado el hecho de que el FRP está ahí durante más de 15 años, tengo la impresión de que este problema de rendimiento podría ser algo muy o incluso inherentemente difícil de resolver al menos dentro de unos años. ¿Es esto cierto?

  3. El mismo autor de la encuesta habla de "fugas de tiempo" en su blog . ¿El problema es exclusivo de FRP o es algo que generalmente tenemos cuando programamos en un lenguaje puro y no estricto? ¿Alguna vez le ha resultado demasiado difícil estabilizar un sistema basado en FRP a lo largo del tiempo, si no es lo suficientemente eficaz?

  4. ¿Sigue siendo un proyecto de nivel de investigación? ¿Las personas, como ingenieros de planta, ingenieros en robótica, ingenieros financieros, etc., las utilizan realmente (en cualquier idioma que se adapte a sus necesidades)?

Aunque personalmente prefiero una implementación de Haskell, estoy abierto a otras sugerencias. Por ejemplo, sería particularmente divertido tener una implementación de Erlang --- ¡entonces sería muy fácil tener un proceso de servidor inteligente, adaptable y de autoaprendizaje!

mnish
fuente

Respuestas:

82

En este momento, existen principalmente dos bibliotecas Haskell prácticas para la programación reactiva funcional. Ambos son mantenidos por personas solteras, pero también están recibiendo contribuciones de código de otros programadores de Haskell:

  • Netwire se centra en la eficiencia, la flexibilidad y la previsibilidad. Tiene su propio paradigma de eventos y se puede utilizar en áreas donde el FRP tradicional no funciona, incluidos los servicios de red y las simulaciones complejas. Estilo: aplicativo y / o con flechas. Autor inicial y mantenedor: Ertugrul Söylemez (este soy yo).

  • reactive-banana se basa en el paradigma tradicional de FRP. Si bien su uso es práctico, también sirve como base para la investigación clásica de FRP. Su enfoque principal está en las interfaces de usuario y hay una interfaz lista para usar para wx. Estilo: aplicativo. Autor inicial y mantenedor: Heinrich Apfelmus.

Debería probar ambos, pero dependiendo de su aplicación, probablemente encontrará que uno u otro se ajusta mejor.

Para juegos, redes, control de robots y simulaciones, Netwire le resultará útil. Viene con cables listos para usar para esas aplicaciones, incluidos varios diferenciales útiles, integrales y mucha funcionalidad para el manejo transparente de eventos. Para ver un tutorial, visite la documentación del Control.Wiremódulo en la página que vinculé.

Para las interfaces gráficas de usuario, actualmente, su mejor opción es reactive-banana. Ya tiene una interfaz wx (como una biblioteca separada reactive-banana-wx) y Heinrich escribe mucho sobre FRP en este contexto, incluyendo ejemplos de código.

Para responder a sus otras preguntas: FRP no es adecuado en escenarios donde necesita previsibilidad en tiempo real. Esto se debe en gran parte a Haskell, pero desafortunadamente FRP es difícil de realizar en lenguajes de nivel inferior. Tan pronto como Haskell esté listo para tiempo real, FRP también lo hará. Conceptualmente, Netwire está listo para aplicaciones en tiempo real.

Las fugas de tiempo ya no son realmente un problema, porque están relacionadas en gran medida con el marco monádico. Las implementaciones prácticas de FRP simplemente no ofrecen una interfaz monádica. Yampa ha comenzado esto y Netwire y reactive-banana se basan en eso.

No conozco ningún proyecto comercial o de gran escala que utilice FRP en este momento. Las bibliotecas están listas, pero creo que la gente todavía no lo está.

ertes
fuente
Excelente respuesta, gracias ... será un ejercicio divertido implementar algunos algoritmos de aprendizaje por refuerzo en la parte superior de su biblioteca.
2012
3
En particular, un juego independiente reciente escrito en Haskell ( Nikki and the Robots ) tomó la decisión de no usar FRP.
Alex R
23

Aunque ya hay algunas buenas respuestas, intentaré responder a sus preguntas específicas.

  1. reactivo no se puede utilizar para proyectos serios, debido a problemas de fugas de tiempo. (ver # 3). La biblioteca actual con el diseño más similar es reactive-banana, que fue desarrollada con reactivo como inspiración, y en discusión con Conal Elliott.

  2. Aunque Haskell en sí no es apropiado para aplicaciones en tiempo real difíciles, es posible usar Haskell para aplicaciones en tiempo real en algunos casos. No estoy familiarizado con la investigación actual, pero no creo que este sea un problema insuperable. Sospecho que sistemas como Yampa, o sistemas de generación de código como Atom, son posiblemente el mejor enfoque para resolver esto.

  3. Una "pérdida de tiempo" es un problema específico del FRP conmutable. La fuga ocurre cuando un sistema no puede liberar objetos viejos porque podría necesitarlos si ocurriera un cambio en algún momento en el futuro. Además de una pérdida de memoria (que puede ser bastante grave), otra consecuencia es que, cuando se produce el cambio, el sistema debe hacer una pausa mientras se recorre la cadena de objetos antiguos para generar el estado actual.

Las bibliotecas frp no conmutables como Yampa y las versiones anteriores de reactive-banana no sufren pérdidas de tiempo. Las bibliotecas frp conmutables generalmente emplean uno de dos esquemas: o tienen una "mónada de creación" especial en la que se crean los valores de FRP, o usan un parámetro de tipo "envejecimiento" para limitar los contextos en los que pueden ocurrir cambios. elerea (¿y posiblemente netwire?) usan el primero, mientras que los recientes reactivos-banana y pomelo usan el segundo.

Por "frp conmutable" me refiero a uno que implementa la función de Conal switcher :: Behavior a -> Event (Behavior a) -> Behavior a, o semántica idéntica. Esto significa que la forma de la red puede cambiar dinámicamente mientras se ejecuta.

Esto realmente no contradice la declaración de @ ertes sobre interfaces monádicas: resulta que proporcionar una Monadinstancia para unEvent hace posible las pérdidas de tiempo, y con cualquiera de los enfoques anteriores ya no es posible definir las instancias equivalentes de Monad.

Finalmente, aunque todavía queda mucho trabajo por hacer con FRP, creo que algunas de las plataformas más nuevas (reactive-banana, elerea, netwire) son lo suficientemente estables y maduras como para que pueda construir código confiable a partir de ellas. Pero es posible que deba dedicar mucho tiempo a aprender los entresijos para comprender cómo obtener un buen rendimiento.

Juan L
fuente
2
Con respecto a las bibliotecas basadas en flechas (Yampa, netwire), también son intercambiables. La razón es que las flechas tienen el envejecimiento incorporado, en realidad no puedes deshacerte de él. (Al ser transformadores de flujo, las flechas son independientes sobre la hora de inicio de su flujo de entrada).
Heinrich Apfelmus
3
No olvides la némesis del plátano reactivo : el sodio .
Dan Burton
1
@HeinrichApfelmus: ese es un punto interesante. En general, no creo que las bibliotecas basadas en flechas sean intercambiables de la misma manera que lo son elerea / pomelo / current-reactive-banana. Creo que su cambio está mucho más cerca de lo que se requería en versiones anteriores de reactive-banana. Sin embargo, esto es solo un presentimiento, no lo he pensado lo suficiente como para describir lo que quiero decir.
John L
2
@DanBurton gracias, estaba tratando de recordar ese nombre sin éxito. Estoy de acuerdo en que el sodio debe considerarse una biblioteca de FRP moderna, aunque no es tan popular como el plátano reactivo.
John L
Aunque la discusión en curso es un poco difícil de seguir, parece indicar que un sistema de tiempo real suave es realmente posible, siempre que el tiempo de GC pueda estar limitado de alguna manera. De todos modos gracias por tu gran respuesta.
Domingo
20

Voy a enumerar un par de elementos en el espacio Mono y .Net y uno del espacio Haskell que encontré no hace mucho tiempo. Empezaré por Haskell.

Olmo - enlace

Su descripción según su sitio:

Elm tiene como objetivo hacer que el desarrollo web front-end sea más agradable. Introduce un nuevo enfoque a la programación de GUI que corrige los problemas sistémicos de HTML, CSS y JavaScript. Elm le permite trabajar rápida y fácilmente con el diseño visual, usar el lienzo, administrar las entradas complicadas del usuario y escapar del infierno de las devoluciones de llamada.

Tiene su propia variante de FRP . Jugando con sus ejemplos, parece bastante maduro.

Extensiones reactivas - enlace

Descripción de su portada:

Reactive Extensions (Rx) es una biblioteca para componer programas asincrónicos y basados ​​en eventos que utilizan secuencias observables y operadores de consulta de estilo LINQ. Con Rx, los desarrolladores representan flujos de datos asincrónicos con Observables, consultan flujos de datos asincrónicos mediante operadores LINQ y parametrizan la simultaneidad en los flujos de datos asincrónicos mediante Programadores. En pocas palabras, Rx = Observables + LINQ + Schedulers.

Reactive Extensions proviene de MSFT e implementa muchos operadores excelentes que simplifican el manejo de eventos. Era código abierto hace solo un par de días. Es muy maduro y usado en producción; en mi opinión, hubiera sido una mejor API para las API de Windows 8 que la que proporciona la biblioteca TPL; porque los observables pueden ser tanto calientes como fríos y reintentados / fusionados, etc., mientras que las tareas siempre representan cálculos calientes o realizados que están en ejecución, fallados o completados.

Escribí código del lado del servidor usando Rx para la asincronía, pero debo admitir que escribir funcionalmente en C # puede ser un poco molesto. F # tiene un par de envoltorios, pero ha sido difícil rastrear el desarrollo de la API, porque el grupo está relativamente cerrado y no lo promueve MSFT como otros proyectos.

Su código abierto vino con el código abierto de su compilador IL-to-JS, por lo que probablemente podría funcionar bien con JavaScript o Elm.

Probablemente podría unir F # / C # / JS / Haskell muy bien usando un agente de mensajes, como RabbitMQ y SocksJS.

Kit de herramientas de interfaz de usuario de Bling - enlace

Descripción de su portada:

Bling es una biblioteca basada en C # para programar fácilmente imágenes, animaciones, interacciones y visualizaciones en WPF / .NET de Microsoft. Bling está orientado a los tecnólogos de diseño, es decir, diseñadores que a veces programan, para ayudar en la creación rápida de prototipos de ricas ideas de diseño de UI. Los estudiantes, artistas, investigadores y aficionados también encontrarán útil a Bling como herramienta para expresar rápidamente ideas o visualizaciones. Las API y las construcciones de Bling están optimizadas para la programación rápida de código desechable en lugar de la programación cuidadosa del código de producción.

Artículo LtU de cortesía .

Probé esto, pero no trabajé con él para un proyecto de cliente. Se ve increíble, tiene una buena sobrecarga de operadores de C # que forman los enlaces entre valores. Utiliza propiedades de dependencia en WPF / SL / (WinRT) como fuentes de eventos. Sus animaciones 3D funcionan bien en hardware razonable. Usaría esto si termino en un proyecto que necesita visualizaciones; probablemente portándolo a Windows 8.

ReactiveUI - enlace

Paul Betts, anteriormente en MSFT, ahora en Github, escribió ese marco. He trabajado con él bastante y me gusta el modelo. Está más desacoplado que Blink (por su naturaleza al usar Rx y sus abstracciones), lo que facilita la prueba unitaria del código que lo usa. El cliente github git para Windows está escrito en este.

Comentarios

El modelo reactivo es lo suficientemente eficaz para la mayoría de las aplicaciones que exigen un rendimiento. Si está pensando en un tiempo real difícil, apuesto a que la mayoría de los lenguajes GC tienen problemas. Rx, ReactiveUI crean una cierta cantidad de objetos pequeños que necesitan ser GCed, porque así es como se crean / eliminan las suscripciones y los valores intermedios progresan en la "mónada" reactiva de las devoluciones de llamada. En general, en .Net prefiero la programación reactiva sobre la programación basada en tareas porque las devoluciones de llamada son estáticas (conocidas en el momento de la compilación, sin asignación) mientras que las tareas se asignan dinámicamente (no se sabe, todas las llamadas necesitan una instancia, se crea basura) y las lambdas se compilan en clases generadas por el compilador.

Obviamente, C # y F # se evalúan estrictamente, por lo que la pérdida de tiempo no es un problema aquí. Lo mismo para JS. Sin embargo, puede ser un problema con los observables reproducibles o en caché.

Henrik
fuente
Gracias por la gran respuesta. Una de las cosas que me gustó de las implementaciones de Haskell FRP es que parecen permitirme desacoplar limpiamente los cálculos para el control u(t)y las simulaciones para f(t). ¿Es ese el caso de las implementaciones de F #?
Domingo
Supongo que se puede decir que esas dos funciones están temporalmente desacopladas, sí. Sin embargo, probablemente no estén desacoplados lógicamente. ;)
Henrik
Hasta donde yo sé, Reactive Extensions y los otros paquetes enfocados en la IU más pulida (y todo lo que está fuera de Haskell, de hecho) usan solo semánticas de eventos, lo que quiere decir que tienen una noción de eventos que puedes activar, pero no una noción de señales de tiempo continuas que pueden interactuar de manera ecuacional. Para construir guis esto está bien, creo. Sin embargo, para construir simulaciones y modelos, esto puede ser desafortunado.
sclv
¿Está insinuando que todas las implementaciones de bibliotecas de programación reactiva funcional necesitan modelar el tiempo de forma continua en lugar de discretamente? Encontré un artículo llamado "Álgebra de procesos con sincronización: tiempo real y tiempo discreto". ¿Es este un buen punto de partida para entender de qué estás hablando?
Henrik
No estoy diciendo que todos lo necesiten, algunos lo hacen, otros no. Pero los que sí lo hacen son más adecuados para ciertas tareas, y los que no lo hacen son más adecuados para otras ...
sclv