Programação Funcional Reativa:
Lidando com código assíncrono
QCon São Paulo, 2014
Leonardo Borges
@leonardo_borges
www.leo...
Sobre
‣ Consultor Senior na ThoughtWorks
Australia
‣ Entusiasta de Programação Functional
‣ Clojure geek
‣ Fundador do Gru...
var result = 1;!
numbers.forEach(function(n){!
if(n % 2 === 0) {!
result *= n;!
}!
});!
console.log( result );!
// 8!
var ...
Em programação imperativa, descrevemos computações como uma
serie de ações que modificam o estado do programa
var result = ...
var result = 1;!
numbers.forEach(function(n){!
if(n % 2 === 0) {!
result *= n;!
}!
});!
console.log( result );!
// 8!
var ...
var result = 1;!
numbers.forEach(function(n){!
if(n % 2 === 0) {!
result *= n;!
}!
});!
console.log( result );!
// 8!
var ...
var result = 1;!
numbers.forEach(function(n){!
if(n % 2 === 0) {!
result *= n;!
}!
});!
console.log( result );!
// 8!
var ...
Já em programação funcional, descrevemos o que queremos fazer e
não como queremos fazê-lo
var numbers = [1,2,3,4,5];
var r...
Já em programação funcional, descrevemos o que queremos fazer e
não como queremos fazê-lo
var numbers = [1,2,3,4,5];
var r...
Já em programação funcional, descrevemos o que queremos fazer e
não como queremos fazê-lo
var numbers = [1,2,3,4,5];
var r...
A programação funcional reativa traz o mesmo princípio
para valores com os quais lidamos no dia-a-dia:
eventos DOM como cl...
Antes de definir um pouco mais
formalmente o que eh FRP, vamos
olhar um exemplo
Movimentos em um jogo
var JUMP = 38, CROUCH = 40,!
LEFT = 37, RIGHT = 39,!
FIRE = 32;!
!
function goRight (){!
$('h1').htm...
Movimentos em um jogo:
estilo imperativo
$(window.document).keyup(function(event){!
switch(event.keyCode){!
case JUMP :!
j...
Temos problemas similares
ao exemplo anterior
Porém, podemos imaginar que key
presses são um stream de teclas
Para a versão funcional, utilizaremos
o framework de FRP RxJS
Movimentos em um jogo:
estilo funcional
var source = Rx.Observable.fromEvent(window.document, 'keyup');
function isKey (ke...
Um pouco mais sobre FRP
‣ Criado em 1997 por Conal Elliott na forma do framework Fran para Haskell
‣ Desde então foi imple...
Exemplo de Behavior: posição do cursor do mouse
function mouseXYBehavior(){!
var behavior = new Rx.BehaviorSubject([0,0]);...
Um pouco mais sobre Rx - Rx 101
Rx.Observable.returnValue(42)!
.map(function(value){ return value * 2; })!
.subscribe(func...
Um pouco mais sobre Rx - Rx 101
Rx.Observable.fromArray([10, 20, 30])!
.map(function(value){ return value * 2; })!
.reduce...
Um pouco mais sobre Rx - Rx 101
function projectRange(n){!
return Rx.Observable.fromArray(_.range(n));!
}!
!
Rx.Observable...
?
Rx.Observable.fromArray([1, 2, 3])
1 2 3
Rx.Observable.fromArray([1, 2, 3])!
.flatMap(projectRange)
projectRange(2)
0 1
projectRange(1)
0
projectRange(3)
0 1 2
0 0...
Combinar Observables dessa forma é uma
técnica poderosa como veremos mais adiante
E comunicação com a rede?
E comunicação com a rede?
‣ Callback hell :(
‣ Promises melhoram um pouco mas tem limitações
‣ Funcionam bem para um nível...
Exemplo: uma simples aplicação de votos
O que queremos:
‣ Mostrar os resultados da pergunta atual
‣ Atualizar os resultados a cada 2 segundos
‣ Se a pergunta atua...
Resultados da parte servidor da aplicação
{!
id: 1,!
question: "Which is the best music style?",!
results: {!
a: 8,!
b: 20...
A idéia principal
Primeiro, tornamos os resultados em um Observable
4 3 3 2 1 1
Depois, duplicamos o Observable, pulando um elemento
4 3 3 2 1 1
5 4 3 3 2 1
skip(1)
Finalmente, zippamos os Observables
4 3 3 2 1 1
5 4 3 3 2 1
zip
[5,4] [4,3] [3,3] [3,2] [2,1] [1,1]
Agora temos acesso em um único observable a
ambos os resultados
Demo
https://github.com/leonardoborges/qcon2014-frp-code
Recapitulando a idéia principal
function resultsObservable () {!
return Rx.Observable.create(function(observer){!
$.get( "...
Recapitulando a idéia principal
function resultsConnectable () {!
var obs = Rx.Observable!
.interval(2000)!
.flatMap(resul...
Recapitulando a idéia principal
function resultsConnectable () {!
var obs = Rx.Observable!
.interval(2000)!
.flatMap(resul...
Recapitulando a idéia principal
function resultsConnectable () {!
var obs = Rx.Observable!
.interval(2000)!
.flatMap(resul...
Show! Será que existe uma forma mais simples de
implementar a mesma funcionalidade?
Certamente!
A mesma funcionalidade, explorando mais da API de Rx
function resultsBuffer () {!
return Rx.Observable!
.interval(2000)!
....
Explore e entenda a fundo a API do seu framework de FRP
favorito: muito provavelmente o que você precisa já foi
implementa...
Em ambas as soluções, não precisamos de uma
variável extra para armazenar a pergunta anterior
Pensar de forma funcional e utilizar um framework
de FRP nos permite implementar soluções simples
e robustas
"FRP is about handling time-varying values like they
were regular values" - Haskell Wiki
(FRP lida com valores que mudam a...
Exemplo bônus: API Reativa para AWS
function resourcesStream (stackName) {!
return Rx.Observable.create(function(observer)...
Exemplo bônus: API Reativa para AWS
function ec2InstanceStream (physicalResourceIds) {!
return Rx.Observable.create(functi...
Exemplo bônus: API Reativa para AWS
function dbInstanceStream (physicalResourceId) {!
return Rx.Observable.create(function...
Exemplo bônus: API Reativa para AWS
resourcesStream('my-stack')!
.filter(isEC2)!
.map(".PhysicalResourceId")!
.reduce([], ...
Exemplo bônus: API Reativa para AWS
var ec2Data = resourcesStream('my-stack')!
.filter(isEC2)!
.map(".PhysicalResourceId")...
Exemplo bônus: API Reativa para AWS
var ec2Data = resourcesStream('my-stack')!
.filter(isEC2)!
.map(".PhysicalResourceId")...
Exemplo bônus: API Reativa para AWS
ec2Data!
.merge(rdsData)!
.reduce([], function(acc, resource) { acc.push(resource); re...
Simples de entender, manter e
testar. FRP #FTW!
Obrigado!
Perguntas?
Leonardo Borges
@leonardo_borges
www.leonardoborges.com
www.thoughtworks.com
Referências
‣ Código: https://github.com/leonardoborges/qcon2014-frp-code
!
‣ RxJS: https://github.com/Reactive-Extensions...
Próximos SlideShares
Carregando em…5
×

Programação functional reativa: lidando com código assíncrono

2.031 visualizações

Publicada em

Palestra da QCon São Paulo, 2014

Publicada em: Tecnologia
0 comentários
3 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

Sem downloads
Visualizações
Visualizações totais
2.031
No SlideShare
0
A partir de incorporações
0
Número de incorporações
15
Ações
Compartilhamentos
0
Downloads
15
Comentários
0
Gostaram
3
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Programação functional reativa: lidando com código assíncrono

  1. 1. Programação Funcional Reativa: Lidando com código assíncrono QCon São Paulo, 2014 Leonardo Borges @leonardo_borges www.leonardoborges.com www.thoughtworks.com
  2. 2. Sobre ‣ Consultor Senior na ThoughtWorks Australia ‣ Entusiasta de Programação Functional ‣ Clojure geek ‣ Fundador do Grupo de Usuários Clojure de Sydney ‣ No momento escrevendo o livro “Clojure Reactive Programming”
  3. 3. var result = 1;! numbers.forEach(function(n){! if(n % 2 === 0) {! result *= n;! }! });! console.log( result );! // 8! var numbers = [1,2,3,4,5]; Em programação imperativa, descrevemos computações como uma serie de ações que modificam o estado do programa
  4. 4. Em programação imperativa, descrevemos computações como uma serie de ações que modificam o estado do programa var result = 1;! numbers.forEach(function(n){! if(n % 2 === 0) {! result *= n;! }! });! console.log( result );! // 8! var numbers = [1,2,3,4,5]; Requer uma variável para armazenar estado
  5. 5. var result = 1;! numbers.forEach(function(n){! if(n % 2 === 0) {! result *= n;! }! });! console.log( result );! // 8! var numbers = [1,2,3,4,5]; Iteramos sobre os itens do array Em programação imperativa, descrevemos computações como uma serie de ações que modificam o estado do programa
  6. 6. var result = 1;! numbers.forEach(function(n){! if(n % 2 === 0) {! result *= n;! }! });! console.log( result );! // 8! var numbers = [1,2,3,4,5]; E na mesma função filtramos os itens… Em programação imperativa, descrevemos computações como uma serie de ações que modificam o estado do programa
  7. 7. var result = 1;! numbers.forEach(function(n){! if(n % 2 === 0) {! result *= n;! }! });! console.log( result );! // 8! var numbers = [1,2,3,4,5]; …e efetuamos a multiplicação. Em programação imperativa, descrevemos computações como uma serie de ações que modificam o estado do programa
  8. 8. Já em programação funcional, descrevemos o que queremos fazer e não como queremos fazê-lo var numbers = [1,2,3,4,5]; var result = numbers! .filter(function(n){ return n % 2 === 0; })! .reduce(function(acc, n){! return acc * n;! });! console.log( result );! // 8!
  9. 9. Já em programação funcional, descrevemos o que queremos fazer e não como queremos fazê-lo var numbers = [1,2,3,4,5]; var result = numbers! .filter(function(n){ return n % 2 === 0; })! .reduce(function(acc, n){! return acc * n;! });! console.log( result );! // 8! Nenhuma variável necessária para a mesma computação
  10. 10. Já em programação funcional, descrevemos o que queremos fazer e não como queremos fazê-lo var numbers = [1,2,3,4,5]; var result = numbers! .filter(function(n){ return n % 2 === 0; })! .reduce(function(acc, n){! return acc * n;! });! console.log( result );! // 8! E temos duas funções que podem ser re-utilizadas de forma independente
  11. 11. A programação funcional reativa traz o mesmo princípio para valores com os quais lidamos no dia-a-dia: eventos DOM como clicks, key presses, movimentos do mouse, chamadas Ajax…
  12. 12. Antes de definir um pouco mais formalmente o que eh FRP, vamos olhar um exemplo
  13. 13. Movimentos em um jogo var JUMP = 38, CROUCH = 40,! LEFT = 37, RIGHT = 39,! FIRE = 32;! ! function goRight (){! $('h1').html("Ir para a direita...");! }! ! function goLeft (){! $('h1').html("Ir para a esquerda...");! }! ! function jump (){! $('h1').html("Pular...");! }! ! function crouch (){! $('h1').html("Abaixar...");! }! ! function fire (){! $('h1').html("Atirar...");! }
  14. 14. Movimentos em um jogo: estilo imperativo $(window.document).keyup(function(event){! switch(event.keyCode){! case JUMP :! jump();! break;! case CROUCH:! crouch();! break;! case LEFT :! goLeft();! break;! case RIGHT :! goRight();! break;! case FIRE :! fire();! break;! };! });
  15. 15. Temos problemas similares ao exemplo anterior
  16. 16. Porém, podemos imaginar que key presses são um stream de teclas
  17. 17. Para a versão funcional, utilizaremos o framework de FRP RxJS
  18. 18. Movimentos em um jogo: estilo funcional var source = Rx.Observable.fromEvent(window.document, 'keyup'); function isKey (keyCode){! return function(event){! return event.keyCode === keyCode;! };! } source.filter(isKey(FIRE)).subscribe(fire);! source.filter(isKey(JUMP)).subscribe(jump);! source.filter(isKey(CROUCH)).subscribe(crouch);! source.filter(isKey(LEFT)).subscribe(goLeft);! source.filter(isKey(RIGHT)).subscribe(goRight);!
  19. 19. Um pouco mais sobre FRP ‣ Criado em 1997 por Conal Elliott na forma do framework Fran para Haskell ‣ Desde então foi implementada em diversas linguagens e frameworks: Rx(.NET| JS|Java), reactive-banana (Haskell), Bacon.js, Elm-lang (compile-to-JS) e outros… ‣ Introduz duas abstrações principais: Behaviors e Events
  20. 20. Exemplo de Behavior: posição do cursor do mouse function mouseXYBehavior(){! var behavior = new Rx.BehaviorSubject([0,0]);! $(window.document).mousemove(function(event){! behavior.onNext([event.clientX, event.clientY]);! });! return behavior;! } var xyBehavior = mouseXYBehavior();! xyBehavior.subscribe(function(xy){! $('h1').html('(' + xy[0] + ',' + xy[1] + ')');! });! ! console.log( xyBehavior.value );!
  21. 21. Um pouco mais sobre Rx - Rx 101 Rx.Observable.returnValue(42)! .map(function(value){ return value * 2; })! .subscribe(function(value){! console.log( value );! });! ! // 84!
  22. 22. Um pouco mais sobre Rx - Rx 101 Rx.Observable.fromArray([10, 20, 30])! .map(function(value){ return value * 2; })! .reduce(function(acc, value){ return acc + value; })! .subscribe(function(value){! console.log( value );! });! ! // 120
  23. 23. Um pouco mais sobre Rx - Rx 101 function projectRange(n){! return Rx.Observable.fromArray(_.range(n));! }! ! Rx.Observable.fromArray([1, 2, 3])! .flatMap(projectRange)! .subscribe(function(value){! console.log( value );! });! ! // 0! // 0! // 1! // 0! // 1! // 2
  24. 24. ?
  25. 25. Rx.Observable.fromArray([1, 2, 3]) 1 2 3
  26. 26. Rx.Observable.fromArray([1, 2, 3])! .flatMap(projectRange) projectRange(2) 0 1 projectRange(1) 0 projectRange(3) 0 1 2 0 0 1 0 1 2
  27. 27. Combinar Observables dessa forma é uma técnica poderosa como veremos mais adiante
  28. 28. E comunicação com a rede?
  29. 29. E comunicação com a rede? ‣ Callback hell :( ‣ Promises melhoram um pouco mas tem limitações ‣ Funcionam bem para um nível de valores ‣ Porém são um mecanismo pobre de composição ‣ E se tivermos uma série de valores que muda ao decorrer do tempo?
  30. 30. Exemplo: uma simples aplicação de votos
  31. 31. O que queremos: ‣ Mostrar os resultados da pergunta atual ‣ Atualizar os resultados a cada 2 segundos ‣ Se a pergunta atual for a mesma que a pergunta anterior, atualizamos o resultado. Senão: ‣ Paramos com a atualização periódica; ‣ Mostramos uma mensagem de countdown; ‣ Mostramos a pergunta anterior com os resultados; ‣ Reiniciamos a atualização periódica
  32. 32. Resultados da parte servidor da aplicação {! id: 1,! question: "Which is the best music style?",! results: {! a: 8,! b: 20,! c: 15! }! }!
  33. 33. A idéia principal
  34. 34. Primeiro, tornamos os resultados em um Observable 4 3 3 2 1 1
  35. 35. Depois, duplicamos o Observable, pulando um elemento 4 3 3 2 1 1 5 4 3 3 2 1 skip(1)
  36. 36. Finalmente, zippamos os Observables 4 3 3 2 1 1 5 4 3 3 2 1 zip [5,4] [4,3] [3,3] [3,2] [2,1] [1,1]
  37. 37. Agora temos acesso em um único observable a ambos os resultados
  38. 38. Demo https://github.com/leonardoborges/qcon2014-frp-code
  39. 39. Recapitulando a idéia principal function resultsObservable () {! return Rx.Observable.create(function(observer){! $.get( "/polls/current/results", function(data) {! observer.onNext(data);! observer.onCompleted();! return function () {! console.log('disposed');! };! });! });! }
  40. 40. Recapitulando a idéia principal function resultsConnectable () {! var obs = Rx.Observable! .interval(2000)! .flatMap(resultsObservable)! .publish()! .refCount();! var obs1 = obs.skip(1);! return Rx.Observable.zipArray(obs, obs1);! }! Tornamos os resultados em um Observable
  41. 41. Recapitulando a idéia principal function resultsConnectable () {! var obs = Rx.Observable! .interval(2000)! .flatMap(resultsObservable)! .publish()! .refCount();! var obs1 = obs.skip(1);! return Rx.Observable.zipArray(obs, obs1);! }! Duplicamos o mesmo, pulando um elemento
  42. 42. Recapitulando a idéia principal function resultsConnectable () {! var obs = Rx.Observable! .interval(2000)! .flatMap(resultsObservable)! .publish()! .refCount();! var obs1 = obs.skip(1);! return Rx.Observable.zipArray(obs, obs1);! }! Finalmente, “zippamos” os Observables
  43. 43. Show! Será que existe uma forma mais simples de implementar a mesma funcionalidade? Certamente!
  44. 44. A mesma funcionalidade, explorando mais da API de Rx function resultsBuffer () {! return Rx.Observable! .interval(2000)! .flatMap(resultsObservable)! .bufferWithCount(2, 1);! }
  45. 45. Explore e entenda a fundo a API do seu framework de FRP favorito: muito provavelmente o que você precisa já foi implementado por alguém
  46. 46. Em ambas as soluções, não precisamos de uma variável extra para armazenar a pergunta anterior
  47. 47. Pensar de forma funcional e utilizar um framework de FRP nos permite implementar soluções simples e robustas
  48. 48. "FRP is about handling time-varying values like they were regular values" - Haskell Wiki (FRP lida com valores que mudam ao decorrer do tempo como se fossem valores regulares)
  49. 49. Exemplo bônus: API Reativa para AWS function resourcesStream (stackName) {! return Rx.Observable.create(function(observer){! cloudFormation.describeStackResources({StackName: stackName}, function(err, data){! if (err) {! observer.onError("Error");! observer.onCompleted();! } else {! observer.onNext(data);! observer.onCompleted();! }! });! });! }
  50. 50. Exemplo bônus: API Reativa para AWS function ec2InstanceStream (physicalResourceIds) {! return Rx.Observable.create(function(observer){! ec2.describeInstances({InstanceIds: physicalResourceIds}, function (err, data) {! if (err) {! observer.onError("Error");! observer.onCompleted();! } else {! observer.onNext(data);! observer.onCompleted();! }! });! });! }
  51. 51. Exemplo bônus: API Reativa para AWS function dbInstanceStream (physicalResourceId) {! return Rx.Observable.create(function(observer){! rds.describeDBInstances({DBInstanceIdentifier: physicalResourceId}, function (err, data) {! if (err) {! observer.onError("Error");! observer.onCompleted();! } else {! observer.onNext(data);! observer.onCompleted();! }! });! });! }
  52. 52. Exemplo bônus: API Reativa para AWS resourcesStream('my-stack')! .filter(isEC2)! .map(".PhysicalResourceId")! .reduce([], function(acc, resource) { acc.push(resource); return acc;})! .flatMap(ec2InstanceStream);!
  53. 53. Exemplo bônus: API Reativa para AWS var ec2Data = resourcesStream('my-stack')! .filter(isEC2)! .map(".PhysicalResourceId")! .reduce([], function(acc, resource) { acc.push(resource); return acc;})! .flatMap(this.ec2InstanceStream); var rdsData = resourcesStream('my-stack')! .filter(isRDS)! .map(".PhysicalResourceId")! .reduce([], function(acc, resource) { acc.push(resource); return acc;})! .flatMap(dbInstanceStream);!
  54. 54. Exemplo bônus: API Reativa para AWS var ec2Data = resourcesStream('my-stack')! .filter(isEC2)! .map(".PhysicalResourceId")! .reduce([], function(acc, resource) { acc.push(resource); return acc;})! .flatMap(this.ec2InstanceStream); var rdsData = resourcesStream('my-stack')! .filter(isRDS)! .map(".PhysicalResourceId")! .reduce([], function(acc, resource) { acc.push(resource); return acc;})! .flatMap(dbInstanceStream);!
  55. 55. Exemplo bônus: API Reativa para AWS ec2Data! .merge(rdsData)! .reduce([], function(acc, resource) { acc.push(resource); return acc;});!
  56. 56. Simples de entender, manter e testar. FRP #FTW!
  57. 57. Obrigado! Perguntas? Leonardo Borges @leonardo_borges www.leonardoborges.com www.thoughtworks.com
  58. 58. Referências ‣ Código: https://github.com/leonardoborges/qcon2014-frp-code ! ‣ RxJS: https://github.com/Reactive-Extensions/RxJS ‣ RxJava: https://github.com/Netflix/RxJava ! Outras implementações: ‣ Bacon.js: https://github.com/baconjs/bacon.js ‣ Reactive Banana: http://www.haskell.org/haskellwiki/Reactive-banana ‣ Elm: http://elm-lang.org/

×