Programação Funcional e Reativa com JavaScript
William Grasel
vivareal.com.br/empresa/carreira/
Programação Funcional
- Programação declarativa
- Isolando efeitos colaterais
- Abstrações Matemáticas
Linguagens "Puramente" Funcionais
- Visam Pureza Matemática
- Isolam efeitos colaterais ao máximo
- Imutáveis por padrão
- Tem funções como tipo primitivo
- Comportamento como parâmetro
Funções "Puras"
- Comportamento consistente
- Pode ser trocada pelo seu resultado final
- Não mantém estado próprio entre chamadas
- Não foge do seu escopo
- Não tem efeitos colaterais
Comportamento Consistente
const double = x => x * 2;
double(2); // 4
double(2); // 4
double(2); // 4
const double = x => x * 2;
let r1 = double(2) + double(4);
let r2 = 4 + 8;
r1 === r2; // true
Efeitos Colaterais
function funcaoNadaPura(obj) {
console.log('io');
return 'concatena'
+ (++obj.num)
+ (++window.qqcoisa);
}
let meuObj = { num: 1 },
r1 = funcaoNadaPura(meuObj),
r2 = funcaoNadaPura(meuObj);
r1 != r2; //false
Por que utilizar
Funções Puras?
- Mais fácil de testar
- Mais fácil de compor
- Diminui complexidade
- Menor taxa de bugs
- Principalmente em programas concorrentes e assíncronos
Funções de Alta Classe
- Padrão de composição de funções
- Recebe uma ou mais funções como argumento
- E / ou retorna uma outra função
Exemplos de Alta Classe
function exibeResultado() {
console.log(arguments);
}
$('button').click(exibeResultado);
$http.get('//url')
.then(exibeResultado);
const a = [1, 2, 3];
const b = a.map(i => i * 10)
// [10, 20, 30]
.filter(i => i > 10)
// [20, 30]
.some(i => i > 25);
// true
f(x) = r
f(x, y, z)
= f(x)
(y)
(z)
Currying
Técnica de passar argumentos para uma função de forma parcial, sob demanda, retornando uma outra função até que todos os argumentos sejam dados.
Exemplos de Currying
function makeArray(a, b, c, d) {
return [a, b, c, d];
}
// Javascript Puro
makeArray
.bind(null, 1)
.bind(null, 2, 3)
(4) // [1, 2, 3, 4]
function makeArray(a, b, c, d) {
return [a, b, c, d];
}
// Com Lodash ou Underscore.js
const curryMA = _.curry(makeArry);
curryMA(1)(2)(3)(4);
curryMA(1, 2)(3, 4);
curryMA(1, 2, 3)(4);
// [1, 2, 3, 4]
const navBar = {
changeTab(newTab, ev){...},
curryTab() {
return _.curry(this.changeTab);
}
}
$('.opt1').click(navBar.curryTab(1));
$('.opt2').click(navBar.curryTab(2));
$('.opt3').click(navBar.curryTab(3));
$('.opt4').click(navBar.curryTab(4));
$('.opt5').click(navBar.curryTab(5));
PERIGO!!!
Zona de grande simplificação de temas complexos e quase religiosos para amantes de programação funcional, os mais preciosistas podem se ofender.
Functors
- É um padrão de transformação de objetos através de funções
- São objetos que tem uma ou mais funções de alta classe
- Que recebe uma função como parâmetro
- Para gerar uma nova versão de si mesmo
- Que seja consistente com a versão anterior
- Permitindo um encadeamento indefinido de transformações
Exemplos conhecidos de Functors
[1, 2, 3]
.map(i => i + 10)
.map(i => i + 'p')
// ['10p', '20p', '30p']
Promise.resolve(1)
.then(i => i + 10)
.then(i => i + 'p')
// Promise {
// [[PromiseStatus]]: "resolved",
// [[PromiseValue]]: '10p'
// }
Falta de consistência
[1, 2, 3]
.comcat([10, 20, 30])
// [1, 2, 3, 10, 20, 30]
.filter(i => i > 10)
// [20, 30]
.some(i => i > 25);
// true
// Exemplos de funções de
// alta classe normalmente
// confundidos com Functors.
Produzindo seu próprio Functor
class OlaMundo {
constructor(val) {
this.val = val;
}
bind(func) {
return new OlaMundo(
func(this.val)
);
}
}
new OlaMundo(1)
.bind(v => v * 10)
.bind(v => v + 'p')
// OlaMundo {
// val: '10p'
// }
Transformando e Achatando Objetos
const array = [1, 2, 3];
const array = [1, 2, 3];
const arrayArray = array
.map( i => [ i*i+1, i*i+2 ] );
// [[2, 3], [4, 5], [6, 7]]
const array = [1, 2, 3];
const arrayArray = array
.map( i => [ i*i+1, i*i+2 ] );
// [[2, 3], [4, 5], [6, 7]]
// JavaScript puro:
arrayArray.reduce((i, newArray) => {
return newArray.concat(i);
}, []);
// [2, 3, 4, 5, 6, 7]
const array = [1, 2, 3];
const arrayArray = array
.map( i => [ i*i+1, i*i+2 ] );
// [[2, 3], [4, 5], [6, 7]]
// JavaScript puro:
arrayArray.reduce((i, newArray) => {
return newArray.concat(i);
}, []);
// [2, 3, 4, 5, 6, 7]
// Com Lodash ou UnderscoreJS:
_.flatten(arrayArray);
const array = [1, 2, 3];
const arrayArray = array
.map( i => [ i*i+1, i*i+2 ] );
// [[2, 3], [4, 5], [6, 7]]
// JavaScript puro:
arrayArray.reduce((i, newArray) => {
return newArray.concat(i);
}, []);
// [2, 3, 4, 5, 6, 7]
// Com Lodash ou UnderscoreJS:
_.flatten(arrayArray);
// Melhor ainda, em uma única linha:
_.flatMap(array, i => [ i*i+1, i*i+2 ]);
Monads
- Mais um padrão de transformação de objetos
- São uma especialização de Functors
- Que ao invés de retornar um tipo simples em cada transformação
- Retorna um objeto do mesmo tipo para cada item transformado
- Por fim junta todos em um só objeto de um mesmo nível
- Permitindo um encadeamento indefinido dessas transformações
Encadeando nosso FlatMap
_.flatMap([], i => [i]);
_.flatMap([], i => [i]);
_.chain([1, 2])
_.flatMap([], i => [i]);
_.chain([1, 2])
.flatMap( i => [ i*i+1, i*i+2 ] )
_.flatMap([], i => [i]);
_.chain([1, 2])
.flatMap( i => [ i*i+1, i*i+2 ] )
.flatMap( i => [ i+1, i*i ] )
_.flatMap([], i => [i]);
_.chain([1, 2])
.flatMap( i => [ i*i+1, i*i+2 ] )
.flatMap( i => [ i+1, i*i ] )
.value()
// [3, 4, 4, 9, 6, 25, 7, 36]
Exemplo conhecido de Monads
Promise.resolve(1)
Promise.resolve(1)
.then( data => 1 * 10 )
Promise.resolve(1)
.then( data => 1 * 10 )
.then( data => $http.get(data) )
Promise.resolve(1)
.then( data => 1 * 10 )
.then( data => $http.get(data) )
.then( data => {
console.log(JSON.stringify(data));
});
Validando Monads
// Identidade a esquerda
Promise.resolve(x).then(fn) == Promise.resolve(fn(x));
// Identidade a direita
Promise.resolve(x).then(x => x) == Promise.resolve(x);
// Associatividade
Promise.resolve(x).then(fn).then(gn)
==
Promise.resolve(x).then(x => {
Promise.resolve(fn(x)).then(Promise.resolve(gn(x)))
});
Programação Reativa
- Paradigma de programação baseado em fluxos assíncronos de dados, também conhecido como Streams
- Mantendo um fluxo unidirecional de interação entre os dados
- De forma declarativa, fácil de ler, entender e manter
- Facilitando a implementação de estados imutáveis
Mas o que são Streams?
- Implementação do PubSub pattern, também conhecidos como Observers, ou simplesmente callback de eventos
- Fluxos de dados assíncronos que transitam pela aplicação
- Diferentes Streams podem ser mergeados e transformados
- Através de paradigmas funcionais como Functor e Monads
Exemplo de Streams
const up = $('#up').asEventStream('click').map(1);
const up = $('#up').asEventStream('click').map(1);
const down = $('#down').asEventStream('click').map(-1);
const up = $('#up').asEventStream('click').map(1);
const down = $('#down').asEventStream('click').map(-1);
const count = up.merge(down)
const up = $('#up').asEventStream('click').map(1);
const down = $('#down').asEventStream('click').map(-1);
const count = up.merge(down)
.scan(0, (x, y) => x + y );
const up = $('#up').asEventStream('click').map(1);
const down = $('#down').asEventStream('click').map(-1);
const count = up.merge(down)
.scan(0, (x, y) => x + y );
count.assign($('#counter'), 'text');
Sera que vale a pena?
Por onde eu começo?
Referencias e Links
Perguntas??
Obrigado!
Copy of Programação Funcional e Reativa com JavaScript
By neuber sousa
Copy of Programação Funcional e Reativa com JavaScript
- 939