Programação funcional para iniciantes

Emanuel G de Souza
Training Center
Published in
8 min readSep 3, 2017
fonte: https://www.linkedin.com/pulse/slow-adoption-functional-programming-banks-abdul-muhit

Este é um texto para iniciantes, mas também para aqueles que pretendem entender os pilares da programação funcional (PF), porque ela é importante e exemplos um tanto reais de seu uso. Para exemplos de código, usarei JavaScript, pois é a linguagem que possuo mais familiaridade. Espero que você goste, e não esqueça de compartilhar a palavra.

O que é Programação Funcional?

É um paradigma de programação, tal como Programação Orientado a Objetos(POO) e Programação Imperativa (existem outros paradigmas, mas esses são os mais famosos). Entende-se como paradigma uma forma de fazer algo. Ou seja, paradigma de programação é o nome que se dá a maneira como se programa, a orientação que seus códigos irão ter. POO, por exemplo, vai me ensinar a modelar meus códigos e algoritmos pensando em entidades que possuem características e comportamentos, vulgo objetos. Em Imperativa, vamos dar ao nosso programa uma sequência de passos para resolver determinado problema. Mas o que seria o paradigma funcional? Quais orientações ele me dá na hora de desenvolver o código?

No paradigma funcional eu não dito ao meu código o que ele deve fazer, quando e como. Não irei desenvolvê-lo passo a passo. Eu penso meu código como uma sequência de funções e/ou passos, as quais de maneira composta irão resolver meu problema.

De maneira simples: código funcional é um código composto de múltiplas funções que se compõem para resolver um problema. Pense da seguinte forma: eu tenho um dado de entrada e preciso transformá-lo em um dado de saída. Usando PF eu vou abstrair as lógicas de transformações do meu código em funções, e usá-las no momento oportuno para transformar este meu dado.

Mas é só isso? Não meu pequeno gafanhoto, essa é só a ponta do Iceberg.

Imutabilidade, pra que te quero?

Lá vai a primeira orientação: não use variáveis, use constantes! Sim, isso mesmo que eu falei, você não vai ter código com uma pancada de variáveis, você vai ter um código mais sucinto com constantes que, via de regra, não irão mudar! Parece coisa de maluco, mas vou te dizer alguns motivos de porquê um código imutável pode ser tão bom.

A imutabilidade faz sentido dentro da programação funcional pelo seu viés matemático. Nela, um número sempre será aquele valor, independente de onde esteja ou como está sendo usado. É importante também entender que nas expressões matemáticas, para um mesmo valor passado a uma variável, teremos o mesmo retorno da função. Ele nunca muda. Se você tem uma expressão como f(x) = x + 2, você pode passar o número 3 quantas vezes quiser, esta função sempre retornará 5. Um último ponto é que o número 3 passado para x, não irá mudar seu valor, ou seja, ele permanece inalterado após o seu uso na função.

Vamos a um pouco de código?

No código acima, nós podemos ver que a variável varMutable pode ser modificada por uma função. Por que isso ocorre? Entre inúmeros motivos, isso ocorre porque a variável está em um escopo compartilhado com a função, ela foi declarada com var, o que faz com que ela fique acessível por todo o código, em um escopo que outras funções terão acesso. Agora, apresento uma refatoração desse código, usando const e fazendo com que a função receba um valor e o retorne.

No código acima podemos ver que:

  • numbernão pode ser acessado fora da função.
  • passei como parâmetro imutableVariable e assim, foi manipulado seu valor.
  • otherImutableVariable recebe o resultado da função pureFunction .

Mais a frente iremos ver o que são funções puras e impuras. Continuemos com a Imutabilidade.

Sem efeitos colaterais

Sim, chega de ficar que nem um maluco procurando aonde uma variável mudou o seu valor e tornou sua vida um Inferno! Em códigos pequenos, nos quais o pequeno gafanhoto está aprendendo a programar, não dão tanta dor de cabeça. Mas imagine um código Angular da vida com algumas dezenas de variáveis GLOBAIS, e você “acidentalmente” modifica uma delas. Maravilha neh?

Um código imutável é um código sem efeitos colaterais. Ou seja, tendo constantes que não podem mudar, você não vai ficar retribuindo valores a variáveis e gerando efeitos colaterais mortais no seu código. Todavia, esse princípio tornou-se um dos motivos para que PF não “engatasse” tanto quanto Imperativa e POO. Motivo: uso de memória, que em programas desenvolvidos usando tal paradigma acabavam consumindo mais que seus “concorrentes”. Hoje, contudo, memória não é um problema. Na maioria dos cenários, é o que mais sobra. O que falta é utilizar o máximo possível do poder de processamento. E aí chegamos ao próximo ponto.

Imutabilidade significa previsibilidade

Quando um programa é desenvolvido usando os princípios da PF, ele se torna previsível. Por que isso é importante? Se lembra que falei do processamento? Então, programas previsíveis significam programas que os processadores vão lidar melhor. Entenda comigo: se eu sei que, ao invocar essa função, eu terei um número de retorno, eu sei que irei lidar com números. E se eu for passar para um outra função um número, mas ela espera uma string, eu já logo acuso um erro. Isso faz com que seu programa se torne mais inteligente… ah, e processadores agradecem!

Um cenário da vida real

Ultimamente tem crescido a adesão a chamada computação paralela, e consequentemente a programação paralela. Nossos processadores estão ficando cada vez mais rápidos, mas não o suficiente para acompanhar algumas evoluções, até mesmo dentro do setor econômico. Um exemplo disso é que algumas empresas tem optado por usar linguagens não bloqueantes, como por exemplo Node.js em seus projetos. Outras tem usados linguagens multi thread para lidar com a grande carga de processamento dos seus dados. Mas já imaginou a implementação de códigos paralelos? Pois bem, não vou me aprofundar no assunto, mas linguagens funcionais tendem a ser mais amigáveis quando o assunto é programação paralela, e um dos motivos é exatamente a imutabilidade! Por que? Códigos previsíveis e sem efeitos colaterais nos ajudam a desenvolver de forma que múltiplos pedaços do nosso sistema rode em múltiplos processadores, e não teremos muitas dores de cabeça.

Abstração acima de tudo!

Abstração é a capacidade de separar o essencial do que não é essencial.

Primeiramente, o que seria, em termos de código, uma abstração? Seria a implementação de uma função para um determinado bloco de código a ser utilizado e reutilizado. Quais as vantagens da abstração?

  • Primeiro, você estará reaproveitando código. Menos código com mais informações. Isso é bom.
  • O processo todo em que as funções foram abstraídas irão fazer sentido para você. Isso, alinhado com um bom nome para as funções, deixarão o seu código muito legível e de fácil compreensão.
  • E aí vem a parte legal. Se lembra que falei acerca da inteligência do código. Então, seu processador já vai saber que dado um número 6, ele terá como retorno um número 5. A previsibilidade aqui faz a festa.

Um exemplo bobo, mas simples do uso de abstrações:

Perceba que no código acima, eu abstraí as condições dos ifs para que até meu código se tornasse mais legível.

Pois bem, quando se fala de abstração, precisamos falar de funções. Agora entraremos no próximo tópico.

Funções, funções, funções...

Não é a toa que programação funcional é a programação “orientada a funções”. O conceito de função matemática permeia este paradigma, como expliquei quando falei de imutabilidade. Agora, se torna necessário eu trazer alguns conceitos importantes quando falamos de funções.

Funções puras

Bem, o conceito de funções puras tem tudo a ver com a não existência de efeitos colaterais. Funções puras são funções que não modificam o escopo ao redor delas. Quando eu aprendi um pouco da Linguagem C, aprendi um conceito muito interessante: ponteiro. E aprendi que é através deles que as funções em C podiam modificar variáveis fora de seu escopo interno. Bem, nós já vimos um exemplo de uma função impura e também um exemplo de uma função pura. Mas quais os princípios por trás? Uma função pura:

  • Recebe ao menos um parâmetro e trabalha com ele.
  • Ela retorna alguma coisa. É interessante que uma função, como nos ensinam, via de regra, é uma sequencia de procedimentos a serem executados. Até ai, tudo bem, porem, em programação funcional uma função sempre deve retornar um valor. Por que? Encadeamento de operações. É normal vermos algo como fn1(fn2(fn3(value))).Parece um tanto estranho isso, mas imagina o seguinte código.

O que a função acima faz? O código legível nos ajuda a compreender as sequencias de passos bem definidos que a expressão acima tem. E além disso, sem as funções retornarem o valor, seria impossível haver esse encadeamento. Um outro motivo é que ao retornar um valor, eu exponho o que deve ser exposto e nunca modifico um escopo de fora, pois se torna necessário eu armazenar este novo valor em algum lugar.

Funções de primeira classe — First Class Functions

Quando uma linguagem possui first-class function, significa que funções podem ser usadas como valores, sendo passadas como parâmetros e recebidas como resultados. Em suma, uma first-class function é uma função que representa um valor no código, pode ser um array, um objeto

Funções de primeira ordem — High Order Functions

Uma linguagem que possui high-order functions, é aquela em que suas funções podem ter outras funções como parâmetros e retornar ou não, elas como resultado.

Currying, ah meu amado currying

Confesso que num primeiro momento eu fiquei tentando entender porque “cargas d’água” isto seria interessante. Até que comecei a trabalhar mais de perto com Vinicius Reis, ai ví que em praticamente tudo, ele usava este bendito currying. Mas que diachos é isso? Na wiki do Haskell, encontramos:

Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.

Vamos entender o código acima, nos atentando as funções getUidAuthorFromTopic e acceptOrder. Antes disso, tenha uma coisa em mente: as funções then e catch das Promises recebem uma função como argumento, tal função recebe como parâmetro o valor de retorno da promessa, seja com Promise.resolve(value) ou Promise.reject(error). Para uma compreensão melhor, leia mais aqui e aqui sobre promises.

Tendo essa ideia em mente, vamos as funções que citei anteriormente. A chave para compreensão de como tudo ocorre está na função curry do ramda.js. Tal função faz o que a frase acima, do wiki do Haskell diz. Mas o que quer dizer essa transformação? Vamos passo a passo:

As funções getUidAuthorFromTopic recebe três parâmetros. Dois parâmetros já foram passados na execução da função. O curry transforma a função que recebe três parâmetros, em uma função que recebe um, quando dois forem passados na hora da execução. O mesmo ocorre com a função acceptOrder, que recebe três parâmetros, mas como foi passado dois, retorna uma outra função que recebe um. Um outro passo a passo:

Bom gente, esse foi mais um texto sobre tecnologia. Espero ter ajudado a sanar algumas dúvidas acerca da programação funcional e seus benefícios. E nunca esqueça: compartilhe a palavra.

Antes de partir, não deixe de dar aquele clap maroto. Ele é um excelente feedback para saber se o texto foi relevante para você, e impulsiona a trazer mais conteúdos. Se tiver alguma crítica a trazer, sinta-se a vontade. Até a próxima!

Sou Emanuel, Desenvolvedor na decision6 e entusiasta de Inteligência Artificial. Maluco por séries e filmes. Amo programação e tudo que a cerca. Conheça meu trabalho no meu site emanuelgsouza.dev e em meu Github. May the force be with you!)

Agradeço imensamente a revisão de Vinicius Reis :)

--

--