Depois de ler muitas páginas sobre o FRP, finalmente me deparei com este texto esclarecedor sobre o FRP, que finalmente me fez entender o que realmente é o FRP.
Cito abaixo Heinrich Apfelmus (autor de banana reativa).
Qual é a essência da programação reativa funcional?
Uma resposta comum seria que “o FRP tem tudo a ver com descrever um sistema em termos de funções que variam no tempo, em vez de um estado mutável”, e isso certamente não estaria errado. Este é o ponto de vista semântico. Mas, na minha opinião, a resposta mais profunda e satisfatória é dada pelo seguinte critério puramente sintático:
A essência da programação reativa funcional é especificar completamente o comportamento dinâmico de um valor no momento da declaração.
Por exemplo, considere o exemplo de um contador: você tem dois botões denominados “Para cima” e “Para baixo” que podem ser usados para aumentar ou diminuir o contador. Imperativamente, você deve primeiro especificar um valor inicial e depois alterá-lo sempre que um botão for pressionado; algo assim:
counter := 0 -- initial value
on buttonUp = (counter := counter + 1) -- change it later
on buttonDown = (counter := counter - 1)
O ponto é que, no momento da declaração, apenas o valor inicial para o contador é especificado; o comportamento dinâmico do contador está implícito no restante do texto do programa. Por outro lado, a programação reativa funcional especifica todo o comportamento dinâmico no momento da declaração, assim:
counter :: Behavior Int
counter = accumulate ($) 0
(fmap (+1) eventUp
`union` fmap (subtract 1) eventDown)
Sempre que você quiser entender a dinâmica do contador, basta analisar sua definição. Tudo o que pode acontecer aparecerá no lado direito. Isso contrasta muito com a abordagem imperativa, na qual declarações subsequentes podem alterar o comportamento dinâmico dos valores declarados anteriormente.
Então, no meu entendimento, um programa de FRP é um conjunto de equações:
j
é discreto: 1,2,3,4 ...
f
depende, t
portanto, isso incorpora a possibilidade de modelar estímulos externos
todo o estado do programa é encapsulado em variáveis x_i
A biblioteca FRP cuida do progresso do tempo, em outras palavras, do j
para j+1
.
Eu explico essas equações com muito mais detalhes neste vídeo.
EDITAR:
Cerca de 2 anos após a resposta original, cheguei recentemente à conclusão de que as implementações de FRP têm outro aspecto importante. Eles precisam (e geralmente resolvem) resolver um problema prático importante: invalidação de cache .
As equações para x_i
-s descrevem um gráfico de dependência. Quando algumas x_i
alterações são alteradas no momento j
, nem todos os outros x_i'
valores j+1
precisam ser atualizados, portanto nem todas as dependências precisam ser recalculadas, porque algumas x_i'
podem ser independentes x_i
.
Além disso, x_i
-s que mudam podem ser atualizados incrementalmente. Por exemplo, vamos considerar uma operação de mapa f=g.map(_+1)
no Scala, onde f
e g
são List
de Ints
. Aqui f
corresponde a x_i(t_j)
e g
é x_j(t_j)
. Agora, se eu acrescentar um elemento a g
ele, seria um desperdício realizar a map
operação para todos os elementos g
. Algumas implementações de FRP (por exemplo, reflex-frp ) visam solucionar esse problema. Esse problema também é conhecido como computação incremental.
Em outras palavras, comportamentos ( x_i
-s) no FRP podem ser pensados como cálculos em cache. É tarefa do mecanismo FRP invalidar e recalcular com eficiência esses cache-s (os x_i
-s) se alguns deles f_i
mudarem.