PROGRAMAÇÃO FUNCIONAL EM JAVASCRIPT
PROGRAMAÇÃO FUNCIONAL EM JAVASCRIPT COMO E POR QUÊ?
Sim, é minha única foto boa e eu uso ela pra tudo. Estudante de Ciência da Computação apaixonado por programação funcional e pesquisador independente (do grego: de mentira) em linguagens de programação. Não tenho muita coisa interessante pra falar de mim mesmo então vou preencher o slide com lorem ipsum dolor sit amet, consectetur adipiscing elit. ARTHUR XAVIER
— BRET VICTOR “A tecnologia muda rápido, mas o modo de pensar das pessoas muda devagar.”
THE FUTURE OF PROGRAMMING BRET VICTOR youtu.be/8pTEmbeENF4 “Pode haver muita resistência a novas formas de trabalho que demandem que você desaprenda o que já aprendeu e pense de forma diferente.”
s : → ℤ = α ↦ 0 se α = ε x + s(β) se α = x • β f : ℤ → ℕ = x ↦ |x|JEITO ESTILO PARADIGMAMINDSET
O QUE É PROGRAMAR?
TRANSFORMAR PROBLEMAPROBLEMAPROBLEMAPROBLEMAPROBLEMA
TRANSFORMAR SOLUÇÃO
PROBLEMAPROBLEMAPROBLEMAPROBLEMAPROBLEMA DECOMPOR
SOLUÇÃO DECOMPOR
MINDSET
λ?
PROGRAMAÇÃO IMPERATIVA PROGRAMAÇÃO DECLARATIVA VS.
// const values : Array<number>; console.log(values.map(x => x*x));
// const values : Array<number>; let squares = []; for (let value of values) { squares.push(value*value); } console.log(squares);
// const names : Array<string>; let promises = []; for (let name of names) { promises.push(name); } Promise.all(promises);
// const names : Array<string>; Promise.all(names.map(getUserByName));
// const userId : string; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; for (let step of steps) { await step(userId); }
// const userId : string; // Promise.sequence = ps => Promise.reduce(ps, (_, p) => p); const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; return Promise.sequence(steps.map(f => f(userId)));
// const userIds : Array<string>; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; for (let userId of userIds) { for (let step of steps) { await step(userId); } }
// const userIds : Array<string>; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; return Promise.sequence(steps.map(step => userIds.map(step)));
// const userIds : Array<string>; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; const promises = steps.map(step => userIds.map(step)); return Promise.sequence(promises);
// const userIds : Array<string>; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; for (let step of steps) { await Promise.all(userIds.map(userId => step(userId)); }
// const userIds : Array<string>; const steps = [ removeUserComments, removeUserPosts, removeUserFriends, removeUserData, ]; const runStepOnUsers = step => Promise.all(userIds.map(step)); return Promise.sequence(steps.map(runStepOnUsers));
PROGRAMAÇÃO IMPERATIVA PROGRAMAÇÃO DECLARATIVA VS.
λ¿
— EU “Paradigma de programação onde computações são representadas por funções ou expressões puras, evitando efeitos colaterais e dados mutáveis, e que utiliza amplamente de composição de funções e funções de primeira classe.”
≠FUNÇÃO PROCEDIMENTO
X Y x f(x) f : X → Y
FUNÇÃO (PURA)
— WIKIPEDIA “Uma função ou expressão produz efeitos colaterais se ela modifica algum estado fora de seu escopo ou realiza alguma interação observável com as funções que a chamaram ou com o mundo externo.”
function append(x) { this.array.push(x); }
function appendTo(array, x) { array.push(x); }
function appendTo(array, x) { return [...array, x]; }
IMUTABILIDADE
function increment(counter) { counter.value += 1; }
function increment(counter) { return { ...counter, value: counter.value + 1, }; }
function increment(counter) { counter = { ...counter, value: counter.value + 1, }; return counter; }
EFEITOS COLATERAIS
function sumOfSquares(array) { let result = 0; for (let x of array) { result += x*x; } console.log(result); };
function sumOfSquares(array) { let result = 0; for (let x of array) { result += x*x; } Database.deleteEverything(); console.log(result); };
function sumOfSquares(array) { let result = 0; for (let x of array) { result += x*x; } return result; }; console.log(sumOfSquares([2, 3, 5]));
const square = x => x*x; const add = (a, b) => a + b; function sumOfSquares(array) { return array.map(square).reduce(add, 0); } console.log(sumOfSquares([2, 3, 5]));
function sumOfSquares(array) { return array.reduce((x, sum) => sum + x*x, 0); } console.log(sumOfSquares([2, 3, 5]));
EFEITOS COLATERAIS?
EFEITOS!
DADOS EFEITOS EFEITOS EFEITOS EFEITOS
COMPOSIÇÃO
function* watchLoadMoreStarred() { while (true) { const {login} = yield take(actions.LOAD_MORE_STARRED); yield fork(loadStarred, login, true); } } function* loadStarred(login, loadMore) { const starredByUser = yield select(getStarredByUser, login); if (!starredByUser || loadMore) yield call(fetchStarred, login, starredByUser.nextPageUrl); }
function* watchLoadMoreStarred() { while (true) { const {login} = yield take(actions.LOAD_MORE_STARRED); yield fork(loadStarred, login, true); } } function* loadStarred(login, loadMore) { const starredByUser = yield select(getStarredByUser, login); if (!starredByUser || loadMore) yield call(fetchStarred, login, starredByUser.nextPageUrl); }
function* watchLoadMoreStarred() { while (true) { const {login} = yield take(actions.LOAD_MORE_STARRED); yield fork(loadStarred, login, true); } } function* loadStarred(login, loadMore) { const starredByUser = yield select(getStarredByUser, login); if (!starredByUser || loadMore) yield call(fetchStarred, login, starredByUser.nextPageUrl); }
function* watchLoadMoreStarred() { while (true) { const {login} = yield take(actions.LOAD_MORE_STARRED); yield fork(loadStarred, login, true); } } function* loadStarred(login, loadMore) { const starredByUser = yield select(getStarredByUser, login); if (!starredByUser || loadMore) yield call(fetchStarred, login, starredByUser.nextPageUrl); }
FUNÇÕES DE PRIMEIRA CLASSE
=FUNÇÃO VALOR
function* watchLoadMoreStarred() { while (true) { const {login} = yield take(actions.LOAD_MORE_STARRED); yield fork(loadStarred, login, true); } } function* loadStarred(login, loadMore) { const starredByUser = yield select(getStarredByUser, login); if (!starredByUser || loadMore) yield call(fetchStarred, login, starredByUser.nextPageUrl); }
FUNÇÕES DE ALTA ORDEMALTA
ABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃO
OTIMIZAÇÃO
ABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃOABSTRAÇÃO
mapfilter reduce
function buildElements(n, factory) { return Array(n).fill(0) .map(Math.random) .map(factory); } appendToDom(10, x => <h2>{x}</h2>);
function buildElements(n, factory) { return Array(n).fill(0) .map(Math.random) .map(factory); } appendToDom(10, h2);
function buildElements(n, factory) { return Array(n).fill(0) .map(compose(factory, Math.random)); } appendToDom(10, h2);
λ!
function getAvatarURL(user) { return Maybe(user) .then(user => user.avatar) .then(avatar => avatar.url); } getAvatarURL({ avatar: { url: 'barbaz' } }); // == Maybe('test') getAvatarURL(null); // == Maybe(null) getAvatarURL({ name: 'foobar' }); // == Maybe(null)
function getAvatarURL(user) { let user; return Maybe(user) .then(_user => { user = _user; return _user.name; }) .then(name => user.hasPage ? nameToURL(name) : null); } generateUserURL({ name: 'foobar', hasPage: true }); // == Maybe('https://fezbok/foobar') generateUserURL({ name: 'barbaz' }); // == Maybe(null) generateUserURL(null); // == Maybe(null) getAvatarURL({ hasPage: true }); // == Maybe(null)
function Do(m, f) { const gen = f(); function doRec(v) { const {value, done} = gen.next(v); const valueM = value instanceof m ? value : m.of(value); return done ? valueM : valueM.then(doRec); } return doRec(undefined); }
function generateUserURL(userMaybe) { return Do(Maybe, function*() { let user = yield userMaybe; let name = yield user.name; return user.hasPage ? nameToURL(name) : null; }); } generateUserURL({ name: 'foobar', hasPage: true }); // == Maybe('https://fezbok/foobar') generateUserURL({ name: 'barbaz' }); // == Maybe(null) generateUserURL(null); // == Maybe(null) getAvatarURL({ hasPage: true }); // == Maybe(null)
// Promise.of = Promise.resolve; Do(Promise, function*() { let hello = yield new Promise(resolve => { setTimeout(() => resolve('Hello'), 1000); }); let world = yield 'world!'; console.log(hello, world); });
// Array.of = a => [a]; // Array.prototype.then = function(f) { // return [].concat.apply([], this.map(f)); // }; Do(Array, function*() { let a = yield [1, 2, 3]; let b = yield ['a', 'b']; console.log(a, b); }); // 1, 'a' // 1, 'b' // 2, 'a' // 2, 'b' // 3, 'a' // 3, 'b'
// Array.of = a => [a]; // Array.prototype.then = function(f) { // return [].concat.apply([], this.map(f)); // }; Do(Array, function*() { let a = yield [1, 2, 3]; let b = yield ['a', 'b']; console.log(a, b); }); // 1, 'a' // 1, 'b' // 2, 'a' // 2, 'b' // 3, 'a' // 3, 'b'
// Array.of = a => [a]; // Array.prototype.then = function(f) { // return [].concat.apply([], this.map(f)); // }; [1, 2, 3].then(a => { ['a', 'b'].then(b => console.log(a, b)); }); // 1, 'a' // 1, 'b' // 2, 'a' // 2, 'b' // 3, 'a' // 3, 'b'
λ!
?
1Evite mutação Use dados e APIs imutáveis e sempre use const ao invés de let e var. Reduz o risco de estado compartilhado;
2Evite dependências implícitas Podem trazer efeitos colaterais indesejados e estado compartilhado. this é uma dependência implícita;
3Desacople dados e comportamento Pense em unidades de trabalho pequenas e genéricas. Abstraia e componha.
— EU “Programação funcional é uma ótima ferramenta para controle de complexidade.”
O(n²)
AbstractSingletonProxyFactoryBeanImpl
1 Difícil de identificar e decompor em partes; 2 Depende de recursos compartilhados; 3 Ligado a uma ordem de execução predefinida. CÓDIGO IMPERATIVO É DIFÍCIL DE TESTAR Difícil de identificar e decompor em partes; Depende de recursos compartilhados; Ligado a uma ordem de execução predefinida.
1 Difícil de identificar e decompor em partes; 2 Depende de recursos compartilhados; 3 Ligado a uma ordem de execução predefinida. CÓDIGO IMPERATIVO É DIFÍCIL DE REFATORAR Difícil de identificar e decompor em partes; Depende de recursos compartilhados; Ligado a uma ordem de execução predefinida.
1 Difícil de identificar e decompor em partes; 2 Depende de recursos compartilhados; 3 Ligado a uma ordem de execução predefinida. CÓDIGO IMPERATIVO É DIFÍCIL DE ENTENDER Difícil de identificar e decompor em partes; Depende de recursos compartilhados; Ligado a uma ordem de execução predefinida.
CÓDIGO FUNCIONAL É FÁCIL DE ENTENDER 1 Difícil de identificar e decompor em partes; 2 Depende de recursos compartilhados;asdasda sdas 3 Ligado a uma ordem de execução predefinida. Força a decomposição de tarefas complexas; Abstrai e generaliza estruturas de controle de fluxo; Completamente previsível.
REQUER DESAPRENDER
REQUER REAPRENDER
Brian Lonsdorf (Dr. Boolean) PROFESSOR FRISBY’S MOSTLY ADEQUATE GUIDE TO FUNCTIONAL PROGRAMMING
Luis Atencio FUNCTIONAL PROGRAMMING IN JAVASCRIPT
Evan Czaplicki ELM
@arthurxavierx @arthur-xavier goo.gl/kL4SNH

JS Experience 2017 - Javascript Funcional