Après avoir lu de nombreuses pages sur FRP, je suis finalement tombé sur cette écriture éclairante sur FRP, cela m'a finalement fait comprendre ce qu'est vraiment FRP.
Je cite ci-dessous Heinrich Apfelmus (auteur de Banane réactive).
Quelle est l'essence de la programmation réactive fonctionnelle?
Une réponse commune serait que «FRP consiste à décrire un système en termes de fonctions variant dans le temps au lieu d'un état mutable», et ce ne serait certainement pas faux. C'est le point de vue sémantique. Mais à mon avis, la réponse la plus profonde et la plus satisfaisante est donnée par le critère purement syntaxique suivant:
L'essence de la programmation réactive fonctionnelle est de spécifier complètement le comportement dynamique d'une valeur au moment de la déclaration.
Par exemple, prenez l'exemple d'un compteur: vous avez deux boutons étiquetés «Up» et «Down» qui peuvent être utilisés pour augmenter ou diminuer le compteur. Impérativement, vous devez d'abord spécifier une valeur initiale, puis la modifier chaque fois qu'un bouton est enfoncé; quelque chose comme ça:
counter := 0 -- initial value
on buttonUp = (counter := counter + 1) -- change it later
on buttonDown = (counter := counter - 1)
Le fait est qu'au moment de la déclaration, seule la valeur initiale du compteur est spécifiée; le comportement dynamique de counter est implicite dans le reste du texte du programme. En revanche, la programmation réactive fonctionnelle spécifie l'ensemble du comportement dynamique au moment de la déclaration, comme ceci:
counter :: Behavior Int
counter = accumulate ($) 0
(fmap (+1) eventUp
`union` fmap (subtract 1) eventDown)
Chaque fois que vous voulez comprendre la dynamique du compteur, il vous suffit de regarder sa définition. Tout ce qui peut lui arriver apparaîtra à droite. Ceci est très différent de l'approche impérative où les déclarations ultérieures peuvent changer le comportement dynamique des valeurs précédemment déclarées.
Donc, à ma connaissance, un programme FRP est un ensemble d'équations:
j
est discret: 1,2,3,4 ...
f
dépend t
donc cela incorpore la possibilité de modéliser des stimuli externes
tous les états du programme sont encapsulés dans des variables x_i
La bibliothèque FRP prend soin de progresser le temps, autrement dit, en prenant j
à j+1
.
J'explique ces équations beaucoup plus en détail dans cette vidéo.
ÉDITER:
Environ 2 ans après la réponse originale, je suis récemment arrivé à la conclusion que les implémentations FRP ont un autre aspect important. Ils doivent résoudre (et le font généralement) un problème pratique important: l' invalidation du cache .
Les équations pour x_i
-s décrivent un graphe de dépendance. Lorsque certaines des x_i
modifications à un moment donné, j
toutes les autres x_i'
valeurs ne j+1
doivent pas être mises à jour, donc toutes les dépendances n'ont pas besoin d'être recalculées car certaines x_i'
peuvent être indépendantes de x_i
.
De plus, les x_i
-s qui changent peuvent être mis à jour progressivement. Par exemple, considérons une opération de carte f=g.map(_+1)
dans Scala, où f
et g
sont List
de Ints
. Ici f
correspond à x_i(t_j)
et g
est x_j(t_j)
. Maintenant, si je lui ajoute un élément, g
il serait inutile d'effectuer l' map
opération pour tous les éléments de g
. Certaines implémentations FRP (par exemple reflex-frp ) visent à résoudre ce problème. Ce problème est également appelé calcul incrémentiel.
En d'autres termes, les comportements (les x_i
-s) dans FRP peuvent être considérés comme des calculs mis en cache. C'est la tâche du moteur FRP d'invalider et de recalculer efficacement ces cache-s (les x_i
-s) si certains des f_i
-s changent.