58
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

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

Embed Size (px)

DESCRIPTION

Palestra da QCon São Paulo, 2014

Citation preview

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

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

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

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”

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

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

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

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

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

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

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

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

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

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

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

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!

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

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

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

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

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

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…

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

Antes de definir um pouco mais formalmente o que eh FRP, vamos

olhar um exemplo

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

Movimentos em um jogovar 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...");!}

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

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;! };!});

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

Temos problemas similares ao exemplo anterior

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

Porém, podemos imaginar que key presses são um stream de teclas

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

Para a versão funcional, utilizaremos o framework de FRP RxJS

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

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);!

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

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

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

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 );!

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

Um pouco mais sobre Rx - Rx 101

Rx.Observable.returnValue(42)! .map(function(value){ return value * 2; })! .subscribe(function(value){! console.log( value );! });!!

// 84!

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

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

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

Um pouco mais sobre Rx - Rx 101function 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

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

?

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

Rx.Observable.fromArray([1, 2, 3])

1 2 3

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

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

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

Combinar Observables dessa forma é uma técnica poderosa como veremos mais adiante

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

E comunicação com a rede?

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

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?

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

Exemplo: uma simples aplicação de votos

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

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

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

Resultados da parte servidor da aplicação

{! id: 1,! question: "Which is the best music style?",! results: {! a: 8,! b: 20,! c: 15! }!}!

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

A idéia principal

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

Primeiro, tornamos os resultados em um Observable

4 3 3 2 1 1

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

Depois, duplicamos o Observable, pulando um elemento

4 3 3 2 1 1

5 4 3 3 2 1

skip(1)

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

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]

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

Agora temos acesso em um único observable a ambos os resultados

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

Demo

https://github.com/leonardoborges/qcon2014-frp-code

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

Recapitulando a idéia principalfunction resultsObservable () {! return Rx.Observable.create(function(observer){! $.get( "/polls/current/results", function(data) {! observer.onNext(data);! observer.onCompleted();! return function () {! console.log('disposed');! };! });! });!}

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

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

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

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

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

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

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

Show! Será que existe uma forma mais simples de implementar a mesma funcionalidade?

Certamente!

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

A mesma funcionalidade, explorando mais da API de Rx

function resultsBuffer () {! return Rx.Observable! .interval(2000)! .flatMap(resultsObservable)! .bufferWithCount(2, 1);!}

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

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

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

Em ambas as soluções, não precisamos de uma variável extra para armazenar a pergunta anterior

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

Pensar de forma funcional e utilizar um framework de FRP nos permite implementar soluções simples

e robustas

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

"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)

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

Exemplo bônus: API Reativa para AWSfunction 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();! }! });! });!}

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

Exemplo bônus: API Reativa para AWSfunction 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();! }! });! });!}

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

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();! }! });! });!}

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

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);!

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

Exemplo bônus: API Reativa para AWSvar 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);!

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

Exemplo bônus: API Reativa para AWSvar 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);!

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

Exemplo bônus: API Reativa para AWS

ec2Data! .merge(rdsData)! .reduce([], function(acc, resource) { acc.push(resource); return acc;});!

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

Simples de entender, manter e testar. FRP #FTW!

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

Obrigado! Perguntas?

Leonardo Borges @leonardo_borges

www.leonardoborges.com www.thoughtworks.com

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

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/