¿Qué es la programación reactiva (funcional)?

Resuelto JtR asked hace 15 años • 18 respuestas

He leído el artículo de Wikipedia sobre programación reactiva . También leí el pequeño artículo sobre programación reactiva funcional . Las descripciones son bastante abstractas.

  1. ¿Qué significa en la práctica la programación reactiva funcional (FRP)?
  2. ¿En qué consiste la programación reactiva (a diferencia de la programación no reactiva)?

Tengo experiencia en lenguajes imperativos/OO, por lo que agradecería una explicación relacionada con este paradigma.

JtR avatar Jun 22 '09 23:06 JtR
Aceptado

Si quieres familiarizarte con FRP, puedes comenzar con el antiguo tutorial de Fran de 1998, que tiene ilustraciones animadas. Para los artículos, comience con Animación reactiva funcional y luego siga los enlaces en el enlace de publicaciones en mi página de inicio y el enlace de FRP en la wiki de Haskell .

Personalmente, me gusta pensar en lo que significa FRP antes de abordar cómo podría implementarse. (El código sin una especificación es una respuesta sin una pregunta y, por lo tanto, "ni siquiera está mal".) Por lo tanto, no describo FRP en términos de representación/implementación como lo hace Thomas K en otra respuesta (gráficos, nodos, bordes, disparo, ejecución, etc). Hay muchos estilos de implementación posibles, pero ninguna implementación dice qué es FRP .

Resueno con la simple descripción de Laurence G de que FRP se trata de "tipos de datos que representan un valor 'a lo largo del tiempo'". La programación imperativa convencional captura estos valores dinámicos sólo indirectamente, a través de estados y mutaciones. La historia completa (pasado, presente, futuro) no tiene representación de primera clase. Además, sólo se pueden capturar (indirectamente) valores que evolucionan discretamente , ya que el paradigma imperativo es temporalmente discreto. Por el contrario, FRP captura estos valores en evolución directamente y no tiene dificultades con los valores en continua evolución.

FRP también es inusual en el sentido de que es concurrente sin entrar en conflicto con el nido de ratas teórico y pragmático que plaga la concurrencia imperativa. Semánticamente, la concurrencia de FRP es detallada , determinada y continua . (Estoy hablando de significado, no de implementación. Una implementación puede implicar o no concurrencia o paralelismo). La determinación semántica es muy importante para el razonamiento, tanto riguroso como informal. Si bien la concurrencia añade una enorme complejidad a la programación imperativa (debido al entrelazado no determinista), en FRP no supone ningún esfuerzo.

Entonces, ¿qué es FRP? Podrías haberlo inventado tú mismo. Comience con estas ideas:

  • Los valores dinámicos/evolutivos (es decir, valores "a lo largo del tiempo") son valores de primera clase en sí mismos. Puede definirlos y combinarlos, pasarlos dentro y fuera de funciones. A estas cosas las llamé "comportamientos".

  • Los comportamientos se construyen a partir de algunas primitivas, como comportamientos constantes (estáticos) y tiempo (como un reloj), y luego con combinaciones secuenciales y paralelas. n comportamientos se combinan aplicando una función n-aria (sobre valores estáticos), "puntualmente", es decir, de forma continua en el tiempo.

  • Para dar cuenta de los fenómenos discretos, hay otro tipo (familia) de "eventos", cada uno de los cuales tiene un flujo (finito o infinito) de sucesos. Cada ocurrencia tiene un tiempo y un valor asociados.

  • Para encontrar el vocabulario compositivo a partir del cual se pueden construir todos los comportamientos y eventos, juegue con algunos ejemplos. Siga deconstruyendo en partes que sean más generales/simples.

  • Para que sepas que estás en terreno sólido, dale a todo el modelo una base de composición, usando la técnica de la semántica denotacional, lo que simplemente significa que (a) cada tipo tiene un correspondiente tipo matemático simple y preciso de "significados", y ( b) cada primitiva y operador tiene un significado simple y preciso en función de los significados de los constituyentes. Nunca, nunca mezcle consideraciones de implementación en su proceso de exploración. Si esta descripción le parece un galimatías, consulte (a) Diseño denotacional con morfismos de clases de tipos , (b) Programación reactiva funcional push-pull (ignorando los bits de implementación) y (c) la página de wikilibros de Haskell sobre semántica denotacional . Tenga en cuenta que la semántica denotacional tiene dos partes, según sus dos fundadores, Christopher Strachey y Dana Scott: la parte de Strachey, más fácil y útil, y la parte de Scott, más difícil y menos útil (para el diseño de software).

Si sigue estos principios, espero que obtenga algo más o menos similar al espíritu de FRP.

¿De dónde saqué estos principios? En diseño de software, siempre hago la misma pregunta: "¿qué significa?". La semántica denotacional me dio un marco preciso para esta pregunta, y uno que se ajusta a mi estética (a diferencia de la semántica operativa o axiomática, las cuales me dejan insatisfecho). Entonces me pregunté ¿qué es el comportamiento? Pronto me di cuenta de que la naturaleza temporalmente discreta del cálculo imperativo es una adaptación a un estilo particular de máquina , más que una descripción natural del comportamiento en sí. La descripción precisa más simple del comportamiento que se me ocurre es simplemente "función del tiempo (continuo)", así que ese es mi modelo. Sorprendentemente, este modelo maneja la concurrencia continua y determinista con facilidad y gracia.

Ha sido todo un desafío implementar este modelo de manera correcta y eficiente, pero esa es otra historia.

Conal avatar Jun 23 '2009 04:06 Conal

En la programación puramente funcional, no hay efectos secundarios. Para muchos tipos de software (por ejemplo, cualquier cosa con interacción del usuario), los efectos secundarios son necesarios en algún nivel.

Una forma de obtener un comportamiento similar a un efecto secundario sin dejar de conservar un estilo funcional es utilizar programación reactiva funcional. Esta es la combinación de programación funcional y programación reactiva. (El artículo de Wikipedia al que vinculó trata sobre este último).

La idea básica detrás de la programación reactiva es que existen ciertos tipos de datos que representan un valor "a lo largo del tiempo". Los cálculos que involucran estos valores que cambian con el tiempo tendrán ellos mismos valores que cambian con el tiempo.

Por ejemplo, podría representar las coordenadas del mouse como un par de valores enteros en el tiempo. Digamos que tenemos algo como (esto es pseudocódigo):

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

En cualquier momento, xey tendrían las coordenadas del mouse. A diferencia de la programación no reactiva, sólo necesitamos realizar esta asignación una vez y las variables x e y permanecerán "actualizadas" automáticamente. Esta es la razón por la que la programación reactiva y la programación funcional funcionan tan bien juntas: la programación reactiva elimina la necesidad de mutar variables y al mismo tiempo le permite hacer mucho de lo que podría lograr con mutaciones de variables.

Si luego hacemos algunos cálculos basados ​​en esto, los valores resultantes también serán valores que cambian con el tiempo. Por ejemplo:

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

En este ejemplo, minXsiempre será 16 menos que la coordenada x del puntero del mouse. Con las bibliotecas reactivas, podría decir algo como:

rectangle(minX, minY, maxX, maxY)

Y se dibujará un cuadro de 32x32 alrededor del puntero del mouse y lo rastreará dondequiera que se mueva.

Aquí hay un artículo bastante bueno sobre programación reactiva funcional .

Laurence Gonsalves avatar Jun 22 '2009 18:06 Laurence Gonsalves