Fundamentos básicos da Programação Funcional
Com exemplos em Swift

Talvez pareça contraintuitivo para você que a imagem inicial de um artigo sobre programação seja um quadro contendo funções matemáticas. Mas, na verdade, programação funcional tem tudo a ver com matemática e suas raízes estão no cálculo lambda (cálculo-λ), que foi um sistema desenvolvido em 1930 para expressar computação usando funções e cujas características foram utilizadas como base para criação da LISP, primeira linguagem de programação dita funcional, ainda em 1950. A seguir, vamos entender quais são estas características dentro do contexto da linguagem Swift.
O paradigma funcional
Programação funcional não é uma linguagem, não é uma sintaxe, mas sim uma maneira de se pensar sobre problemas e sua resolução. É um paradigma diferente.
Um paradigma de programação fornece e determina a visão que o programador possui sobre a estruturação e execução do programa. Por exemplo, em programação orientada a objetos, pode-se abstrair um programa como uma coleção de objetos (instâncias que representam uma abstração de entidades) que interagem entre si, enquanto em programação imperativa os programadores abstraem o programa como uma sequência de ações executadas de modo sequencial.
Por sua vez, o paradigma funcional é caracterizado basicamente pela ausência de efeitos colaterais em sua execução. É um paradigma que trata a computação como uma avaliação de funções matemáticas, e que evita estados ou dados mutáveis. Ela enfatiza a aplicação de funções para resolver um dado problema, em contraste da programação imperativa, que enfatiza alterações no estado. Para ficar mais claro, vamos a um exemplo do que é um código funcional comparado a um imperativo:
Imperativo
Funcional
Percebe a diferença? No código não funcional acima, a função increment() está modificando além de seu escopo interno, alterando o valor de dados (variável a) fora dela. No contexto de programação funcional isto não deve ocorrer, como vimos na definição.
Além disso, o paradigma funcional é declarativo, o que significa que a programação é feita com expressões ou declarações ao invés de statements (unidades sintáticas de uma linguagem de programação imperativa que expressam alguma ação a ser executada).
Os pilares da programação funcional
Programar funcionalmente não é apenas o que foi mostrado acima, vamos agora aprender conceitos importantes que fazem parte deste paradigma.
Pure Functions
O primeiro conceito fortemente relacionado ao paradigma funcional é o de funções puras.
Para uma função f ser considerada pura, o resultado da mesma depende apenas dos argumentos que são passados para ela, ou seja, ao chamar f duas vezes com o mesmo valor para um argumento x produz o mesmo resultado f(x) em ambas as execuções, isso está em contraste com procedimentos que dependem de um estado local ou global, podendo produzir resultados diferentes em momentos diferentes. Portanto, uma função pura não modifica valores fora de seu escopo, não produz efeitos colaterais e pode ser facilmente reutilizada ou composta com outras funções para a resolução de um dado problema.
Dada esta definição, revisite os códigos de exemplo acima e perceba que a primeira função increment() se trata de uma função impura enquanto a segunda implementação está dentro da definição e, portanto, é uma função pura.
É importante ressaltar algo que no exemplo simples não foi notório. Imagine que você está criando uma função complexa, que em seu escopo utiliza vários métodos. Neste tipo de situação é necessária atenção ao que inserir no corpo de sua função para que ela seja, de fato, pura.
Higher Order Functions
Em linguagens que suportam programação funcional, como é o caso de Swift, funções são valores. E funções de alta ordem são simplesmente funções que operam em outras funções, ou seja, podem receber uma função como entrada ou retornar uma função como saída.
A biblioteca padrão de Swift conta com diversas funções de alta ordem, como por exemplo filter, map e reduce. Apenas para entender o comportamento, vamos a um exemplo utilizando a função filter, que itera sobre uma coleção e retorna uma coleção filtrada de acordo com o critério desejado.
Imagine que você possui o seguinte código não-funcional:
O exemplo é bem simples, queremos um array a partir do original que contenha apenas os elementos ímpares. Vamos ver como isto é feito funcionalmente com o filter.
A condição para realizar o filtro dos elementos ímpares é inserida na closure entre chaves ({}) e de maneira sucinta a função realiza a tarefa. Esta é a beleza das funções de alta ordem, toda a lógica que inserimos foi a da linha:
return number % 2 != 0
E o filter é responsável por toda a iteração na coleção original e inserção na nova coleção, tudo que precisamos é passar a condição desejada.
Podemos ainda tornar nosso código mais elegante e reutilizável.
Viu só? Desacoplamos nossa condição tornando-a independente. Se em qualquer outro lugar do código quisermos utilizá-la, is_odd estará lá disponível.
Recursion
Recursão também é um conceito muito importante envolvido com programação funcional. Uma função é dita recursiva quando ela chama a si mesma, agindo como um loop, ou seja, executando o mesmo código várias vezes dado um critério de parada que é chamado de caso base. Exemplo:
Utilizar a recursão como alternativa aos loops se mostra relevante em um contexto funcional pois loops requerem um estado mutável em sua execução. Isso significa que deve haver uma variável mutável, que é proibida em um ambiente puramente funcional. Aqui vale ressaltar que, não é o caso de Swift, mas linguagens puramente funcionais, como Haskell, são projetadas para operar sem a necessidade de iteração, respeitando a natureza do paradigma funcional.
Diferenciais do paradigma funcional
Agora que cobrimos as principais nuances que, juntas, caracterizam a programação funcional, algumas vantagens merecem ser destacadas antes do final desse artigo.
Código funcional é extremamente modular
Como funções são as bases do paradigma funcional, imagine ter várias funções curtas e que, juntas, irão se compor pra resolver o seu problema, podendo ser reutilizadas sempre que necessário.
Código funcional é imutável e seguro
Já conversamos bastante sobre funções puras serem livres de modificações fora de escopo, este é um dos motivos que torna a programação funcional bastante segura para o programador.
Além disso, este paradigma não encoraja a criação de variáveis, mas de constantes, o que dá maior controle ao programador sobre o que de fato está acontecendo. Imagine uma situação: ao escrever código para um sistema multi-thread, se duas ou mais threads acessarem a mesma variável simultaneamente, elas poderão modificá-la ou acessá-la fora de ordem, causando inconsistências. Se você pudesse escrever código onde o estado nunca sofreu uma mutação, esses problemas desapareceriam. Sistemas que funcionam assim têm estado imutável, o que significa que o estado não pode mudar durante o curso de um programa.
Código funcional é fácil de testar
A partir de tudo que foi dito anteriormente, um código puro é também fácil de testar. Funções com ausência de efeitos colaterais criam para o programador um cenário no qual não existe mais a necessidade de fazer mocks em todo o código para testá-lo.
Código funcional é legível
Imagine que seu programa possui milhares de linhas de códigos, funções e variáveis, ter visão sobre tudo que está acontecendo se torna bastante dispendioso. O programador de funcional irá lidar com código mais curto e simples, com funções bem definidas e sem mudanças de estado. Ou seja, código legível.
Próximos passos
Conteúdos interessantes que podem ajudá-lo a dar continuidade ao estudo de programação funcional:
- Para se aprofundar sobre algumas funções de alta ordem da biblioteca padrão de Swift
- Artigo completo e atualizado (fev/2019) para Swift 4.0 contendo o passo a passo para começar a programar funcional e como escrever código declarativo, em vez de imperativo
- Entendo melhor sobre o poder das higher order functions e como criar as próprias
Obrigada por ter chegado até aqui e bons estudos! :)