Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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...
Upcoming SlideShare
Loading in …5
×

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

2,448 views

Published on

Palestra da QCon São Paulo, 2014

Published in: Technology

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/

×