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:
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?
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?
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?
¿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!
Aunque ya hay algunas buenas respuestas, intentaré responder a sus preguntas específicas.
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.
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.
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
Monad
instancia 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.
fuente
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:
Tiene su propia variante de FRP . Jugando con sus ejemplos, parece bastante maduro.
Extensiones reactivas - enlace
Descripción de su portada:
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:
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é.
fuente
u(t)
y las simulaciones paraf(t)
. ¿Es ese el caso de las implementaciones de F #?