Concrete Logo
Hamburger button

Programação funcional em Javascript – desconstruindo o Pareto.js

  • Blog
  • 7 de Maio de 2016
Share

*Post originalmente publicado no Medium pessoal do autor. Confira aqui.

Nos últimos tempos só se fala em programação funcional, seus benefícios, funções puras, dados imutáveis, composição de funções, etc. Caso você queira saber mais, recomendo esse post do Matheus Lima.

Atualmente temos diversas libs que auxiliam o Javascript na missão de ser funcional, como Lodash, Underscore e Ramda. Neste post especificamente, vou falar sobre o Pareto.js, lib que criamos na Concrete Solutions que é simples como o Princípio de Pareto, tem por objetivo ser leve e resolver 80% dos seus problemas com 20% de código.

Geralmente procuro aprender algo que desmistifique a “mágica” por trás da implementação. Foi assim quando comecei a aprender Angular, e agora estou aplicando o mesmo à programação funcional. Por isso, nesse post vamos avaliar as implementações de Curry e Compose do Pareto.js.

Curry

Curry é a ação de pegar uma função que receba múltiplos argumentos e transformá-la em uma cadeia de funções, em que cada uma receba somente um parâmetro.

Vamos agora ver o teste dessa função:

Para começar a desmitificar a mágica, temos duas perguntas:

  • Como a nossa função Curry irá armazenar os parâmetros já passados?
  • O que o Function.prototype.bind() tem a ver com isso?

Function.prototype.bind()

Comumente usamos .bind() para passarmos um contexto para executar uma função, mas nos esquecemos de algo importante, como dito na documentação do developer.mozilla.org:

Partial Functions: The next simplest use of bind() is to make a function with pre-specified initial arguments. These arguments (if any) follow the provided this value and are then inserted at the start of the arguments passed to the target function…

Resumindo:

Um dos usos de bind() é construir uma função com argumentos iniciais pré-especificados. Esses argumentos serão passados após o valor de This e serão inseridos no início dos argumentos passados para a função de destino.

Difícil de entender? Então vamos a mais um exemplo (em ES5 para que você possa abrir o devtools e já testar).

Reparem que a função myNumbers espera três parâmetros. A cada vez que chamamos .bind(this, val), a função retornada pelo método .bind() automagicamente guarda o argumento passado.

E com isso chegamos à implementação do curry no pareto.js, que irá chamar curry.bind(this, fn, …args), empilhando os parâmetros no spread operator …args até que a quantidade de argumentos seja a mesma que a função espera (args.length === fn.length). Caso não tenha entendido o que é …args, dê uma lida em spread operator.

Compose

Como o próprio nome sugere, Compose significa construir funções mais complexas por meio de funções mais simples, compondo-as. Vamos à implementação no Pareto.js:

Vamos ao teste dessa função:

E aí temos uma pergunta:

  • O que Array.prototype.reduce() está fazendo aí no meio ?

Array.prototype.reduce()

Em geral, pensamos no .reduce() como um acumulador, porém somente no sentido de soma de valores e não de composição. Sabemos que o .reduce() aplica uma função de callback sobre um acumulador, varrendo todos os elementos do array. Vamos começar a desconstrução do nosso compose:

  • Sabemos que ele recebe um array de funções como argumentos por meio do spread operator …args;
  • A função de callback do .reduce(), que será executada sobre cada item do nosso array pode receber até 4 parâmetros, sendo eles: previousValue, currentValue, index e array. Porém, aqui só iremos utilizar os dois primeiros (previousValue e currentValue). Lembrando que na primeira chamada à nossa função de callback, previousValue será o valor do primeiro elemento do array e currentValue será o valor do elemento seguinte;
  • A nossa função de callback irá compor a função passada em previousValue com a que está em currentValue, adicionando na declaração da função que ela poderá receber N argumentos (…args). Resultando em previousValue(currentValue(…args)).

De acordo com os nossos testes, vamos observar os passos de execução em uma tabela:

Chamadas da função de callback do .reduce()

Chamadas da função de callback do .reduce()

Com isso, temos o resultado da função mais interna (moreExclaim) alimentando as funções mais externas (exclaim e depois to UpperCase).

E é isso! Espero que tenha ajudado a entender a relação de curry e compose com .bind() e .reduce(). Feedbacks são mais do que bem-vindos e incentivados, aproveite os campos abaixo. Até a próxima!

É desenvolvedor e quer fazer parte do nosso time? Clique aqui!

Algumas referências:
https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
https://medium.com/@matheusml/entendendo-programa%C3%A7%C3%A3o-funcional-em-javascript-de-uma-vez-c676489be08b#
https://github.com/concretesolutions/pareto.js
https://pt.wikipedia.org/wiki/Princ%C3%ADpio_de_Pareto