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