Mudança de Mentalidade: Do Paradigma Orientado a Objetos para o Funcional
Disclaimer Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de uma aula do curso "Os 3 pilares para escalar sistemas distribuídos" da Jornada Dev + Eficiente. Se preferir acompanhar por vídeo, é só dar o play. Introdução No episódio de hoje quero falar sobre mudança de mentalidade quando você sai do paradigma orientado a objetos para o funcional, mas desse caminho para o outro, não tanto do funcional para o orientado a objetos. Essa reflexão surgiu a partir de uma pergunta que me fizeram no canal: "Foi muito difícil para você modificar o jeito que você pensa quando você foi começar a programar com Clojure?" Minha resposta foi surpreendente até para mim mesmo: "Vou confessar para você que para mim não mudou nada. Eu continuo querendo escrever o mesmo tipo de programa, só que uma hora eu escrevo esse programa com Java e outra hora eu escrevo esse programa com uma linguagem como Clojure ou qualquer outra que seja puramente funcional." Principais Preocupações na Transição entre Paradigmas Decidi aprofundar essa reflexão e listei as principais preocupações que uma pessoa pode ter quando sai de uma linguagem orientada a objetos para uma linguagem funcional. Vamos analisar cada uma delas. 1. Mudança de Mentalidade Passar do pensamento em termos de objetos e estados para pensar em termos de funções e transformações de dados. Sinceramente, não fico pensando muito nisso. Antes, eu tinha uma classe com atributos e métodos que recebem parâmetros e fazem alguma coisa. Agora, tenho uma função que recebe parâmetros com os dados que ela vai operar, e várias funções que muitas vezes recebem esse mesmo parâmetro em contextos diferentes. Para mim, não muda muita coisa. 2. Imutabilidade Adaptar-se a não modificar dados, mas criar novas versões transformadas. Novamente, isso não mudou muito para mim. Meu código orientado a objetos já era muito influenciado pela ideia de imutabilidade, não no sentido extremo de sempre criar novas versões de objetos, mas no sentido de: "Recebi um parâmetro e não vou mexer no estado daquele parâmetro porque não sei de onde ele veio, não sei se alguém já mexeu ou vai querer mexer naquele estado depois da minha função." As ideias de minimizar a alteração de estado e concentrá-la em um ponto específico já eram comuns no meu código. Obviamente, em Clojure você não tem a opção de ficar alterando estados, mas não é porque tenho essa opção em Java ou JavaScript que preciso usá-la. 3. Funções Puras Compreender e aplicar o conceito de funções sem efeitos colaterais. Eu já era influenciado por esse conceito. Se tenho uma função que recebe um parâmetro e preciso salvar esse valor no banco de dados, não me permito fazer isso na função intermediária, pois não sei se quem chamou antes já salvou. Naturalmente, minhas funções em linguagens como Java e JavaScript já se aproximam de funções puras. Não são puras no sentido completo, pois uma função pura é aquela que não gera nenhum tipo de efeito colateral e sempre retorna o mesmo resultado para os mesmos parâmetros. Por exemplo, se você faz um log dentro da função ou instancia uma data, tecnicamente a função deixa de ser pura. Mas essa já era uma preocupação que eu tinha. 4. Recursão Substituir loops tradicionais por chamadas recursivas para iteração. Esse ponto realmente exige um esforço cognitivo maior. Não era acostumado a fazer loops com recursão. No entanto, com o tempo usando Clojure, percebi que quase todo loop em coleções já está implementado em funções prontas como map, filter, reduce, ou na composição dessas funções. Isso também existe no Java moderno. Você raramente precisa construir um algoritmo recursivo complexo na lógica de negócio cotidiana. 5. Funções de Ordem Superior Dominar o uso de funções como parâmetro. Eu já usava bastante programando em Java, inclusive na Jornada Dev + Eficiente tem uma parte do curso onde falo sobre utilizar templates de método recebendo funções como argumento. Além disso, já programava muito em JavaScript, então passar funções como argumento para ganhar testabilidade já era comum para mim. 6. Composição de Funções Aprender a combinar funções pequenas em vez de métodos grandes. Isso não faz muito sentido como diferença entre paradigmas para mim. Da mesma forma que é interessante ter funções pequenas, também é interessante ter métodos com tamanho limitado em programação orientada a objetos. 7. Pensamento Declarativo Focar no "quê" em vez do "como", descrevendo o resultado desejado. Acho que na prática, a teoria é um pouco diferente. Quando estou codando em Clojure, ainda sinto que estou dizendo o que eu quero que aconteça, assim como em Java. Alguém poderia dizer que estou apenas declarando estruturas que o interpretador vai executar à sua maneira, enquanto no Java estou dizendo exatamente como fazer, mas para mim não faz muita diferença. Senti essa diferenç

Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de uma aula do curso "Os 3 pilares para escalar sistemas distribuídos" da Jornada Dev + Eficiente. Se preferir acompanhar por vídeo, é só dar o play.
Introdução
No episódio de hoje quero falar sobre mudança de mentalidade quando você sai do paradigma orientado a objetos para o funcional, mas desse caminho para o outro, não tanto do funcional para o orientado a objetos. Essa reflexão surgiu a partir de uma pergunta que me fizeram no canal: "Foi muito difícil para você modificar o jeito que você pensa quando você foi começar a programar com Clojure?"
Minha resposta foi surpreendente até para mim mesmo: "Vou confessar para você que para mim não mudou nada. Eu continuo querendo escrever o mesmo tipo de programa, só que uma hora eu escrevo esse programa com Java e outra hora eu escrevo esse programa com uma linguagem como Clojure ou qualquer outra que seja puramente funcional."
Principais Preocupações na Transição entre Paradigmas
Decidi aprofundar essa reflexão e listei as principais preocupações que uma pessoa pode ter quando sai de uma linguagem orientada a objetos para uma linguagem funcional. Vamos analisar cada uma delas.
1. Mudança de Mentalidade
Passar do pensamento em termos de objetos e estados para pensar em termos de funções e transformações de dados.
Sinceramente, não fico pensando muito nisso. Antes, eu tinha uma classe com atributos e métodos que recebem parâmetros e fazem alguma coisa. Agora, tenho uma função que recebe parâmetros com os dados que ela vai operar, e várias funções que muitas vezes recebem esse mesmo parâmetro em contextos diferentes. Para mim, não muda muita coisa.
2. Imutabilidade
Adaptar-se a não modificar dados, mas criar novas versões transformadas.
Novamente, isso não mudou muito para mim. Meu código orientado a objetos já era muito influenciado pela ideia de imutabilidade, não no sentido extremo de sempre criar novas versões de objetos, mas no sentido de: "Recebi um parâmetro e não vou mexer no estado daquele parâmetro porque não sei de onde ele veio, não sei se alguém já mexeu ou vai querer mexer naquele estado depois da minha função."
As ideias de minimizar a alteração de estado e concentrá-la em um ponto específico já eram comuns no meu código. Obviamente, em Clojure você não tem a opção de ficar alterando estados, mas não é porque tenho essa opção em Java ou JavaScript que preciso usá-la.
3. Funções Puras
Compreender e aplicar o conceito de funções sem efeitos colaterais.
Eu já era influenciado por esse conceito. Se tenho uma função que recebe um parâmetro e preciso salvar esse valor no banco de dados, não me permito fazer isso na função intermediária, pois não sei se quem chamou antes já salvou.
Naturalmente, minhas funções em linguagens como Java e JavaScript já se aproximam de funções puras. Não são puras no sentido completo, pois uma função pura é aquela que não gera nenhum tipo de efeito colateral e sempre retorna o mesmo resultado para os mesmos parâmetros. Por exemplo, se você faz um log dentro da função ou instancia uma data, tecnicamente a função deixa de ser pura. Mas essa já era uma preocupação que eu tinha.
4. Recursão
Substituir loops tradicionais por chamadas recursivas para iteração.
Esse ponto realmente exige um esforço cognitivo maior. Não era acostumado a fazer loops com recursão. No entanto, com o tempo usando Clojure, percebi que quase todo loop em coleções já está implementado em funções prontas como map, filter, reduce, ou na composição dessas funções. Isso também existe no Java moderno. Você raramente precisa construir um algoritmo recursivo complexo na lógica de negócio cotidiana.
5. Funções de Ordem Superior
Dominar o uso de funções como parâmetro.
Eu já usava bastante programando em Java, inclusive na Jornada Dev + Eficiente tem uma parte do curso onde falo sobre utilizar templates de método recebendo funções como argumento. Além disso, já programava muito em JavaScript, então passar funções como argumento para ganhar testabilidade já era comum para mim.
6. Composição de Funções
Aprender a combinar funções pequenas em vez de métodos grandes.
Isso não faz muito sentido como diferença entre paradigmas para mim. Da mesma forma que é interessante ter funções pequenas, também é interessante ter métodos com tamanho limitado em programação orientada a objetos.
7. Pensamento Declarativo
Focar no "quê" em vez do "como", descrevendo o resultado desejado.
Acho que na prática, a teoria é um pouco diferente. Quando estou codando em Clojure, ainda sinto que estou dizendo o que eu quero que aconteça, assim como em Java. Alguém poderia dizer que estou apenas declarando estruturas que o interpretador vai executar à sua maneira, enquanto no Java estou dizendo exatamente como fazer, mas para mim não faz muita diferença.
Senti essa diferença mais claramente quando surgiram as bibliotecas de código reativo para Java, onde você declarava funções e como elas se mapeavam, e retornava essa declaração para a biblioteca executar (de forma síncrona ou assíncrona, em um ou vários núcleos, com retry, etc). Mas no dia a dia de codificação, as situações não se apresentam assim. Tenho um fluxo de negócio para implementar, faço várias chamadas de funções, e não estou declarando nada abstrato - estou dizendo que quero que seja feito daquele jeito.
8. Processamento de Coleções Funcionais
Substituir loops por operações com map, filter e reduce.
Isso já é assim em praticamente todas as linguagens modernas. Todas permitem fazer essas operações com funções e métodos prontos.
9. Ausência de Estruturas de Controle Tradicionais
Adaptar-se à falta de loops for/while convencionais.
Isso parece repetitivo com o ponto 4 e 8. Sobre construções como if/else, alguém poderia dizer que em linguagens funcionais o if é uma função, mas no código continua sendo um if - um mecanismo que executa algo condicionalmente. Para mim, não faz diferença se é uma função ou não.
10. Currying e Aplicação Parcial
Compreender técnicas específicas de programação funcional.
Também já tinha usado isso em Java. Se tenho uma função com três parâmetros e só consigo resolver parte deles, resolvo e retorno outra função que espera o parâmetro restante. De novo, não mudou muito meu jeito de pensar.
Conclusão
No final, não sinto uma diferença grande entre os paradigmas. Acho que isso se deve ao fato de que eu já aplicava muitos princípios do mundo funcional ao meu código orientado a objetos.
Pessoalmente, gosto da ideia de ter uma classe onde junto valores que quero usar regularmente com as funções que operam sobre esses valores. Gosto de coesão, e uma linguagem orientada a objetos me entrega isso praticamente de graça. Barbara Liskov propôs justamente isso: juntar uma estrutura de dados com o conjunto de funções que operam sobre ela regularmente.
Por outro lado, também gosto das ideias das linguagens funcionais: minimizar efeitos colaterais, concentrar esses efeitos em um ponto específico, ter funções o mais puras possível (o que facilita testes), e passar funções como argumentos para ganhar flexibilidade.
Como minha linguagem mais proficiente é Java, já tinha absorvido muitos desses princípios funcionais para programar nela. Por isso, não senti muita diferença quando fiz a transição.
Conheça a Jornada Dev + Eficiente
A Jornada Dev + Eficiente é um treinamento focado em fazer você crescer na carreira como uma pessoa cada vez mais especializada em Entregar Software que Gera Valor com o Máximo de Qualidade e Fluidez.
A Jornada pavimenta este caminho através de uma abordagem integrada, trabalhando diversos aspectos que influenciam na qualidade da entrega final, tais como: Engenharia de Requisitos, Design de Código, Arquitetura, Testes etc.
É o único local que você vai encontrar que é 100% focado em fazer você crescer como uma pessoa desenvolvedora de software completa.
Para conhecer mais, acesse https://deveficiente.com