Paradigmas de programação funcional no JavaScript moderno: Function Composition

Nos dois capítulos anteriores, passamos por alguns dos paradigmas básicos da programação funcional – funções puras e imutabilidade. Se você ainda não leu, pode começar por aqui .

Neste artigo, expandiremos nosso conhecimento de funções e suas propriedades, estabelecendo as bases de como elas podem ser usadas juntas para compor a lógica do seu programa.

O objetivo da programação funcional é construir nossos programas usando principalmente pequenas funções puras que podem ser montadas juntas como se você estivesse combinando pequenas peças lego.

Qual é a composição da função?

A composição da função é o ato de combinar múltiplas funções juntas para criar fluxos lógicos mais complexos. Ao usar essa técnica, podemos evitar estruturas de código iterativo que são lidas como um fluxo de operações e, em vez disso, abstrair a complexidade do processo, combinando as instruções em uma única função.

Não se deixe intimidar pela duração da última sentença. Para simplificar, aqui começaremos a aprender como combinar as funções juntas.

Pelo que aprendemos em um dos artigos anteriores – as funções devem ser pequenas, puras e não gerar efeitos colaterais. Uma função que adere a essas condições é uma função pura e funções puras são fáceis de compor.

As funções sempre retornam o mesmo tipo e, dados os mesmos argumentos, retornam o mesmo valor, o que os torna previsíveis. E, ao contrário dos filmes, você quer que suas funções tenham um resultado previsível para que você possa usá-las facilmente em sua base de código.

A pré-condição que precisamos ter em mente é que, quando compomos as funções, cada uma deve pegar a saída da anterior. Não podemos apenas pegar algumas funções, juntá-las e esperar que funcionem – suas necessidades de entrada e saída devem corresponder .

Parabéns, você acaba de ter sua primeira experiência com composição de funções!

Você pode imaginar a composição da função como uma combinação de tubos. A execução começa do primeiro (o mais à direita) e o que for produzido será passado para o segundo. A segunda função então pega isso, faz algo com ele e passa para a próxima função e assim por diante.

Posteriormente no artigo, encontraremos outra maneira de compor as funções juntas, mas para este exemplo em particular, lembre-se de que a execução começa de dentro para fora ou da direita para a esquerda – o que for mais fácil para você lembrar

Agora vamos voltar às precondições que precisamos ter em mente ao usar a composição de funções. Se você tem uma função que usa um número como um argumento, você não pode compor com um que retorna uma string. Bem, tecnicamente você pode, mas você não conseguirá o resultado desejado.

Outra maneira simples de explicar a composição é que o resultado de chamar consecutivamente as funções deve ser igual ao resultado da composição das mesmas.

Ao provar que ambas as abordagens têm o mesmo resultado, temos certeza de que compusemos nossas funções adequadamente e elas não contam com nenhuma entrada extra.

Note também que não estamos apenas passando a função interna como um argumento, isso seria uma função de ordem superior. A função mais interna está sendo chamada com seus argumentos.

Onde é aplicado?

Sempre que estou aprendendo algo novo, gosto de considerar onde posso aplicá-lo e como ele pode ser útil.

Recentemente, tive um caso em que uma API esperava receber uma carga útil JSON formatada de uma maneira específica. Uma maneira de fazer isso é implementar a lógica de formatação no próprio código, mas isso não é algo que você realmente deseja. Não importa qual serviço ou aplicativo você esteja construindo, você não quer que o interior de seu aplicativo dependa de algo externo.

Então, decidi implementar uma etapa extra antes de despachar a carga para analisá-la e prepará-la. A abordagem mais simples aqui é criar uma função parsePayload que parsePayload o objeto e retorne um novo pronto para ser enviado.

Minha preocupação era que o formato requerido da API do cliente pudesse mudar, então decidi dividir as operações em múltiplas funções pequenas e ser mais flexível quando se trata de modificação no futuro. Outro grande benefício disso é a clareza do processo de análise.

Mesma sintaxe de antes, mas dividida em novas linhas

Neste exemplo em particular, começamos pelo filterEmpty . Não se esqueça de que com essa maneira particular de compor a execução é sempre de dentro para fora. Depois de formatValues as propriedades vazias, retornamos o objeto para a função formatValues , que formatValues esse objeto e fará o que seu nome sugere. Ele retorna o objeto formatado para mapKeys que faz algum trabalho nas chaves e, em seguida, acaba sendo stringed para que possa ser enviado.

Se estivéssemos trabalhando com uma matriz, isso poderia ser alcançado com os métodos incorporados no objeto JavaScript Array, mas isso torna mais claro para futuros desenvolvedores que herdam a base de código exatamente o que eu estava tentando fazer.

Uma explicação mais matemática

Embora eu tente não dar explicações matemáticas, a composição de funções é um conceito que é facilmente descrito com um exemplo de matemática.

Imagine que você tenha algum cálculo x + 3 e você queira multiplicar seu resultado por 10 – você escreveria isso como (x + 3) * 10 . Veja como podemos implementar facilmente isso usando a composição de funções

Parece bem intuitivo né? Pode ser lido em voz alta e fará perfeito sentido o que você está tentando fazer – some os números 3 e 5 e multiplique por 10 . Este é o exemplo que fez a composição de funções clicar para mim e acredito que ilustra melhor como o código funcional descritivo pode ser.

Por que isso é uma coisa?

A primeira vez que você lê sobre isso, você provavelmente se pergunta por que é necessário se você pode usar algum tipo de encadeamento. O JavaScript tem muitas funções utilitárias para seus tipos de dados, portanto, não é difícil modificar matrizes ou cadeias de caracteres.

Para encadear para o trabalho, no entanto, você precisa estar passando o mesmo objeto, porque você não está apenas chamando uma função, você está chamando-a no objeto que é passado da anterior. Portanto, você precisa desse objeto para incluir todas as funções que deseja encadear.

Em um caso em que você deseja filtrar e mapear uma matriz, o encadeamento é a abordagem melhor e mais legível. Mas, em um cenário no qual você espera que o tipo de dados seja alterado ou esteja implementando um fluxo complexo que não pode ser suportado com os métodos incorporados, a composição ainda pode beneficiá-lo sem sacrificar a legibilidade.

Bibliotecas

Existem muitas bibliotecas JS construídas com a programação funcional em mente, mas para este exemplo estaremos usando o Ramda. É a escolha perfeita se você quiser testar a água e ver como você se sentirá usando uma abordagem mais funcional para construir seus programas.

Por simplicidade, vamos usar um exemplo com algumas operações computacionais.

Este exemplo é bem simples e usa apenas três funções, então ainda é compreensível. No entanto, se precisarmos compor cinco funções, ela sairá do controle e se tornará mais difícil de ler. Podemos sempre dividir as funções em linhas diferentes, mas devemos ter cuidado para não exagerar, especialmente quando a contagem das funções compostas aumenta.

Para evitar confusão desnecessária, podemos usar a função de compose de Ramda. O benefício de usar uma biblioteca é que a sintaxe pode ser facilmente entendida e pesquisada, de forma que os colegas que não se divertiram com a programação funcional ainda possam entender o que exatamente está acontecendo sem depender diretamente de você.

A primeira diferença que você pode ver aqui é que dentro da chamada de composição não estamos fornecendo os argumentos para a primeira função. De fato, compor retornará uma nova função que podemos chamar. Além disso, lembre-se de que as funções criadas com o composto são executadas por último para o primeiro – o que significa que a sum será executada primeiro, seguida por square e depois addTen . Se você quiser que eles sejam executados do primeiro ao último, você pode usar a função de pipe fornecida pelo Ramda. A sintaxe é 100% a mesma, com a diferença sendo a ordem de execução.

Por que a composição da função é importante?

A composição de funções está no centro da programação funcional e ensinará as noções básicas necessárias para criar funções e fluxos poderosos. O que aprendemos neste artigo apenas arranha a superfície do tópico, mas serve ao propósito de deixá-lo mais confortável com a manipulação de funções

Qual é o próximo?

Nós cobrimos os conceitos básicos da composição de funções e você provavelmente deve se sentir à vontade para compor suas funções usando o Ramda ou o JavaScript antigo.

Este é o momento em que o vendedor diz “Mas espere, tem mais!”. E com certeza há mais – combinando o que aprendemos neste artigo com alguns outros conceitos de programação funcional – currying e aplicação parcial , podemos criar abstrações incrivelmente poderosas e desvendar o verdadeiro potencial das funções.

No próximo artigo, vamos analisar a aplicação parcial e o que há de tão especial nisso!

Paradigmas de programação funcional no JavaScript moderno: funções puras
JavaScript é uma das linguagens de programação mais populares por aí. Ele roda no navegador, no desktop, no celular… hackernoon.com
Paradigmas de programação funcional no JavaScript moderno: Imutabilidade
Este é o segundo capítulo de uma série de artigos sobre paradigmas práticos de programação funcional. Se você ainda não leu… hackernoon.com

Texto original em inglês.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *