Qual é o status das implementações atuais de Functional Reactive Programming?


90

Estou tentando visualizar alguns sistemas físicos automáticos simples (coisas como pêndulo, braços de robô, etc.) em Haskell. Muitas vezes, esses sistemas podem ser descritos por equações como

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

onde u(t)representa algum tipo de 'controle inteligente'. Esses sistemas parecem se encaixar muito bem no paradigma da Programação Reativa Funcional.

Então eu peguei o livro "The Haskell School of Expression" de Paul Hudak, e descobri que a linguagem de domínio específico "FAL" (para Functional Animation Language) apresentada lá realmente funciona muito bem para meus sistemas de brinquedo simples (embora algumas funções, notavelmente integrate, parecia ser um pouco preguiçoso para um uso eficiente, mas facilmente corrigível).

Minha pergunta é: qual é a alternativa mais madura, atualizada, bem mantida e ajustada para o desempenho para aplicações mais avançadas ou mesmo práticas hoje?

Esta página wiki lista várias opções para Haskell, mas não tenho certeza sobre os seguintes aspectos:

  1. O status de "reativo", o projeto de Conal Eliott que é (como eu o entendo) um dos inventores deste paradigma de programação, parece um pouco obsoleto. Eu amo seu código, mas talvez eu devesse tentar outras alternativas mais atualizadas? Qual é a principal diferença entre eles, em termos de sintaxe / desempenho / estabilidade de tempo de execução?

  2. Para citar uma pesquisa em 2011, Seção 6, " ... as implementações de FRP ainda não são eficientes ou previsíveis o suficiente em desempenho para serem usadas com eficácia em domínios que exigem garantias de latência ... ". Embora a pesquisa sugira algumas possíveis otimizações interessantes, dado o fato de que o FRP existe há mais de 15 anos, tenho a impressão de que esse problema de desempenho pode ser algo muito ou mesmo inerentemente difícil de resolver pelo menos em alguns anos. Isso é verdade?

  3. O mesmo autor da pesquisa fala sobre "vazamentos de tempo" em seu blog . O problema é exclusivo do FRP ou algo que geralmente enfrentamos ao programar em uma linguagem pura e não estrita? Você já achou muito difícil estabilizar um sistema baseado em FRP ao longo do tempo, se não apresentar desempenho suficiente?

  4. Este ainda é um projeto de nível de pesquisa? As pessoas, como engenheiros de fábrica, engenheiros de robótica, engenheiros financeiros, etc., estão realmente os usando (em qualquer linguagem que atenda às suas necessidades)?

Embora eu pessoalmente prefira uma implementação de Haskell, estou aberto a outras sugestões. Por exemplo, seria particularmente divertido ter uma implementação Erlang --- seria muito fácil ter um processo de servidor inteligente, adaptável e de autoaprendizagem!

Respostas:


82

No momento, existem principalmente duas bibliotecas Haskell práticas para programação funcional reativa. Ambos são mantidos por pessoas solteiras, mas também estão recebendo contribuições de código de outros programadores Haskell:

  • A Netwire se concentra na eficiência, flexibilidade e previsibilidade. Ele tem seu próprio paradigma de evento e pode ser usado em áreas onde o FRP tradicional não funciona, incluindo serviços de rede e simulações complexas. Estilo: aplicativo e / ou seta. Autor e mantenedor inicial: Ertugrul Söylemez (sou eu).

  • banana reativa baseia - se no paradigma FRP tradicional. Embora seja prático de usar, também serve como base para pesquisas clássicas de FRP. Seu foco principal está nas interfaces de usuário e há uma interface pronta para o wx. Estilo: aplicativo. Autor e mantenedor inicial: Heinrich Apfelmus.

Você deve tentar os dois, mas dependendo de sua aplicação, provavelmente descobrirá que um ou outro se encaixa melhor.

Para jogos, rede, controle de robô e simulações, você achará o Netwire útil. Ele vem com fios prontos para essas aplicações, incluindo vários diferenciais úteis, integrais e muitas funcionalidades para tratamento transparente de eventos. Para um tutorial, visite a documentação do Control.Wiremódulo na página que vinculei.

Para interfaces gráficas de usuário, atualmente, sua melhor escolha é banana reativa. Ele já tem uma interface wx (como uma biblioteca separada reativa-banana-wx) e Heinrich bloga muito sobre FRP neste contexto, incluindo exemplos de código.

Para responder às suas outras perguntas: O FRP não é adequado em cenários onde você precisa de previsibilidade em tempo real. Isso se deve em grande parte a Haskell, mas infelizmente o FRP é difícil de realizar em idiomas de nível inferior. Assim que o próprio Haskell estiver pronto para o tempo real, o FRP também chegará lá. Conceitualmente, a Netwire está pronta para aplicativos em tempo real.

Os vazamentos de tempo não são mais um problema, porque estão amplamente relacionados à estrutura monádica. Implementações práticas de FRP simplesmente não oferecem uma interface monádica. Yampa começou isso e a Netwire e a banana reativa se basearam nisso.

Não conheço nenhum projeto comercial ou em grande escala usando FRP no momento. As bibliotecas estão prontas, mas acho que as pessoas não estão - ainda.


Ótima resposta, obrigado .. será um exercício divertido implementar alguns algoritmos de aprendizado por reforço na sua biblioteca.
final de

3
Notavelmente, um jogo indie recente escrito em Haskell ( Nikki and the Robots ) tomou a decisão de não usar FRP.
Alex R

23

Embora já existam algumas respostas boas, tentarei responder às suas perguntas específicas.

  1. reativo não é utilizável para projetos sérios, devido a problemas de vazamento de tempo. (ver # 3). A biblioteca atual com o design mais semelhante é reativa-banana, que foi desenvolvida tendo reativo como inspiração, e em discussão com Conal Elliott.

  2. Embora o próprio Haskell seja impróprio para aplicativos de tempo real de disco rígido, é possível usar o Haskell para aplicativos de tempo real de software em alguns casos. Não estou familiarizado com as pesquisas atuais, mas não acredito que este seja um problema intransponível. Suspeito que sistemas como o Yampa ou sistemas de geração de código como Atom são possivelmente a melhor abordagem para resolver isso.

  3. Um "vazamento de tempo" é um problema específico do FRP comutável. O vazamento ocorre quando um sistema é incapaz de liberar objetos antigos porque pode precisar deles se uma troca ocorrer em algum momento no futuro. Além de um vazamento de memória (que pode ser bastante grave), outra consequência é que, quando a troca ocorre, o sistema deve pausar enquanto a cadeia de objetos antigos é percorrida para gerar o estado atual.

Bibliotecas frp não comutáveis, como Yampa e versões mais antigas de banana reativa, não sofrem com vazamentos de tempo. Bibliotecas frp comutáveis ​​geralmente empregam um de dois esquemas: ou têm uma "mônada de criação" especial na qual os valores de FRP são criados ou usam um parâmetro de tipo "envelhecimento" para limitar os contextos nos quais as trocas podem ocorrer. elerea (e possivelmente netwire?) usa o primeiro, enquanto a banana reativa recente e a toranja usam o último.

Por "frp comutável", quero dizer aquele que implementa a função de Conal switcher :: Behavior a -> Event (Behavior a) -> Behavior a, ou semântica idêntica. Isso significa que a forma da rede pode mudar dinamicamente à medida que é executada.

Isso realmente não contradiz a afirmação de @ertes sobre interfaces monádicas: acontece que fornecer uma Monadinstância para um Eventtorna possível o vazamento de tempo, e com qualquer uma das abordagens acima não é mais possível definir as instâncias Monad equivalentes.

Finalmente, embora ainda haja muito trabalho a ser feito com o FRP, acho que algumas das plataformas mais novas (banana reativa, elerea, netwire) são estáveis ​​e maduras o suficiente para que você possa construir um código confiável a partir delas. Mas talvez você precise gastar muito tempo aprendendo os meandros para entender como obter um bom desempenho.


2
Com relação às bibliotecas baseadas em setas (Yampa, netwire), elas também podem ser trocadas. A razão é que as flechas têm o envelhecimento embutido, você não pode realmente se livrar dele. (Sendo transformadores de stream, as setas são agnósticas quanto ao horário de início de seu stream de entrada.)
Heinrich Apfelmus

3
Não se esqueça do nêmesis da banana reativa : o sódio .
Dan Burton

1
@HeinrichApfelmus: esse é um ponto interessante. Geralmente não penso em bibliotecas baseadas em setas como selecionáveis ​​da mesma forma que elerea / grapefruit / current-reattive-banana. Acho que a troca deles está muito mais próxima do que era exigido nas versões anteriores do banana reativa. Este é apenas um pressentimento, porém, eu não pensei o suficiente para sequer descrever o que quero dizer.
John L

2
@DanBurton obrigado, eu estava tentando sem sucesso lembrar esse nome. Eu concordo que o sódio deve ser considerado uma biblioteca FRP moderna, embora não seja tão popular quanto a banana reativa.
John L

Embora a discussão em andamento seja um pouco difícil de acompanhar, parece indicar que um sistema soft-realtime é de fato possível, desde que o tempo de GC possa de alguma forma ser limitado. De qualquer forma, obrigado pela sua ótima resposta.
final de

20

Vou listar alguns itens no espaço Mono e .Net e um no espaço Haskell que encontrei não muito tempo atrás. Vou começar com Haskell.

Elm - link

Sua descrição de acordo com seu site:

Elm tem como objetivo tornar o desenvolvimento web front-end mais agradável. Ele apresenta uma nova abordagem para a programação GUI que corrige os problemas sistêmicos de HTML, CSS e JavaScript. O Elm permite que você trabalhe de forma rápida e fácil com o layout visual, use a tela, gerencie entradas complicadas do usuário e escape do inferno de callback.

Ele tem sua própria variante de FRP . Brincando com seus exemplos, parece bem maduro.

Extensões reativas - link

Descrição da primeira página:

O Reactive Extensions (Rx) é uma biblioteca para compor programas assíncronos e baseados em eventos usando sequências observáveis ​​e operadores de consulta no estilo LINQ. Usando Rx, os desenvolvedores representam fluxos de dados assíncronos com Observables, consultam fluxos de dados assíncronos usando operadores LINQ e parametrizam a simultaneidade nos fluxos de dados assíncronos usando Agendadores. Simplificando, Rx = Observables + LINQ + Schedulers.

As extensões reativas vêm do MSFT e implementam muitos operadores excelentes que simplificam o tratamento de eventos. isso foi código aberto apenas alguns dias atrás. É muito maduro e usado na produção; na minha opinião, teria sido uma API melhor para as APIs do Windows 8 do que a biblioteca TPL fornece; porque os observáveis ​​podem ser quentes e frios e repetidos / mesclados etc., enquanto as tarefas sempre representam cálculos quentes ou concluídos que estão em execução, com falha ou concluídos.

Escrevi código do lado do servidor usando Rx para assincronização, mas devo admitir que escrever funcionalmente em C # pode ser um pouco chato. F # tem alguns wrappers, mas tem sido difícil rastrear o desenvolvimento da API, porque o grupo é relativamente fechado e não é promovido pela MSFT como outros projetos.

Seu código-fonte aberto veio com o código-fonte aberto de seu compilador IL-to-JS, portanto, provavelmente poderia funcionar bem com JavaScript ou Elm.

Você provavelmente poderia vincular F # / C # / JS / Haskell muito bem usando um agente de mensagens, como RabbitMQ e SocksJS.

Bling UI Toolkit - link

Descrição da primeira página:

Bling é uma biblioteca baseada em C # para facilmente programar imagens, animações, interações e visualizações no WPF / .NET da Microsoft. O Bling é orientado para tecnólogos de design, ou seja, designers que às vezes programam, para ajudar na prototipagem rápida de ideias ricas de design de IU. Estudantes, artistas, pesquisadores e amadores também acharão o Bling útil como uma ferramenta para expressar ideias ou visualizações rapidamente. As APIs e construções de Bling são otimizadas para a programação rápida de código descartável, em oposição à programação cuidadosa do código de produção.

Artigo LtU de cortesia .

Eu testei isso, mas não trabalhei com isso para um projeto de cliente. Parece incrível, tem uma ótima sobrecarga de operadores C # que formam as ligações entre os valores. Ele usa propriedades de dependência em WPF / SL / (WinRT) como fontes de eventos. Suas animações 3D funcionam bem em hardware razoável. Eu usaria isso se acabasse em um projeto que precisasse de visualizações; provavelmente portando-o para o Windows 8.

ReactiveUI - link

Paul Betts, anteriormente na MSFT, agora no Github, escreveu essa estrutura. Trabalhei com ele extensivamente e gosto do modelo. É mais desacoplado do que o Blink (por sua natureza de usar Rx e suas abstrações) - tornando mais fácil testar a unidade de código usando-o. O cliente github git para Windows está escrito nisso.

Comentários

O modelo reativo tem desempenho suficiente para a maioria dos aplicativos que exigem desempenho. Se você está pensando em tempo real difícil, aposto que a maioria das linguagens GC tem problemas. Rx, ReactiveUI criam alguma quantidade de pequenos objetos que precisam ser GCed, porque é assim que as assinaturas são criadas / descartadas e os valores intermediários progridem na "mônada" reativa de retornos de chamada. Em geral em .Net eu prefiro a programação reativa à programação baseada em tarefas porque os retornos de chamada são estáticos (conhecidos no tempo de compilação, sem alocação) enquanto as tarefas são alocadas dinamicamente (não conhecido, todas as chamadas precisam de uma instância, lixo criado) - e lambdas compilam em classes geradas por compilador.

Obviamente, C # e F # são avaliados estritamente, portanto, o vazamento de tempo não é um problema aqui. O mesmo para JS. Pode ser um problema com observáveis ​​reproduzíveis ou em cache.


Obrigado pela ótima resposta. Uma das coisas de que gostei nas implementações de FRP de Haskell é que elas parecem me permitir desacoplar de forma clara os cálculos para o controle u(t)e as simulações f(t). É esse o caso das implementações F #?
final de

Eu acho que você pode dizer que essas duas funções estão temporariamente desacopladas, sim. Eles provavelmente não estão logicamente dissociados. ;)
Henrik

Pelo que eu sei, as extensões reativas e os outros pacotes focados em IU mais polidos (e tudo fora de Haskell, na verdade) usam apenas semântica de eventos - o que significa que eles têm uma noção de eventos que você pode ativar, mas não uma noção de sinais de tempo contínuo que podem interagir equacionalmente. Para construir guis isso é bom, eu acho. No entanto, para construir simulações e modelos, isso pode ser lamentável.
sclv

Você está insinuando que todas as implementações de lib de programação reativa funcional precisam modelar o tempo continuamente, em vez de discretamente? Eu encontrei um artigo chamado "Processo de Álgebra com Timing: Tempo Real e Tempo Discreto" - este é um bom ponto de partida para entender do que você está falando?
Henrik de

Não estou dizendo que todos precisam - alguns precisam, outros não. Mas aqueles que o fazem são mais adequados para certas tarefas, e aqueles que não são mais adequados para outras ...
sclv
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.