Este é um post disponível para assinantes MVPou para quem possui Créditos DevMedia. Clique aqui para saber mais!
Artigo Java Magazine 56 - Programação Funcional (ou quase) em Java
Artigo da Revista Java Magazine - Edição 56.

Programação Funcional (ou quase) em Java
Saiba como – e porquê – adotar técnicas de Programação Funcional na plataforma Java
Aplicando o estilo de programação funcional em Java, e examinando a linguagem Scala
O leitor que acompanha esta coluna já deve conhecer meu apreço por técnicas de programação funcional, que tenho comentado em diversos artigos, por exemplo, ao falar de concorrência e outras situações beneficiadas por esse paradigma. Muitos leitores devem imaginar: esse Osvaldo deve ser um veterano de LISP ou Scheme, que nunca se conformou com linguagens imperativas... longe da verdade. É fato que experimentei algumas linguagens funcionais, quase sempre em cursos universitários. Não cheguei a dominar nenhuma linguagem dessas, o que é impossível sem uma boa experiência prática, desenvolvendo aplicações reais. Sendo que o mercado de desenvolvimento de software no qual atuo dá pouca oportunidade para aventuras com linguagens alternativas.
O que acontece é o oposto – sou um especialista
Como já gastei algumas páginas desta revista com propaganda do paradigma funcional, resolvi dar o serviço completo e explicar o estilo de programação Java funcional “light” que tenho aplicado cada vez mais aos meus projetos. Pra completar, termino com uma olhada preliminar de Scala, uma nova linguagem de programação para a plataforma Java SE que combina as vantagens de OO e programação funcional.
Por quê a programação funcional importa?
Para começar fundamentando minha tese, não há palavras melhores do que as escritas por John Hughes em seu célebre paper de 1984, Why Functional Programming Matters. Resumo aqui somente o essencial. Na verdade, vou resumir tanto que só apresentarei um único argumento.
A maior vantagem da programação funcional é sua modularidade. Em toda a evolução das LPs, vemos que essa qualidade é fundamental. Na evolução de linguagens “espaguete” como Fortran para as estruturadas como C e Pascal, as novas linguagens permitiam organizar um programa em procedimentos e estruturas de dados customizadas. Estes artefatos bem definidos e isolados ficavam mais simples de entender, reusáveis, fáceis de dar manutenção, etc.
No próximo passo evolutivo, as linguagens OO investiram novamente na modularidade. Uma classe permite unificar, de forma muito poderosa, algoritmos e dados relacionados. O polimorfismo potencializa essa modularização, permitindo que o cliente de uma classe manipule de maneira uniforme toda uma hierarquia de classes derivadas. Por exemplo, um método iterator() pode ser usado de forma homogênea para centenas de classes compatíveis com Collection.
Estes avanços foram importantes, mas agem numa única dimensão: a estrutural. Substituímos variáveis globais por parâmetros, GOTO por WHILE, cascatas de ifs ou switch por polimorfismo, etc. Mas no fundo, estamos programando do mesmo jeito que antes, só com mais ordem e facilidade.
Estado mutável
Há outra dimensão na qual podemos avançar na modularidade. Começaremos definindo o conceito de estado. Um processo possui um estado, que podemos definir, informalmente, como o conjunto de valores de todas as variáveis existentes a qualquer momento. Mais precisamente: o conteúdo total do heap, e dos stacks de todos os threads.
Um efeito colateral é toda ação do programa que produz alteração do seu estado. Num código como x = 10; ++x, a segunda instrução, o ++x, é um efeito colateral. Também chamamos isso de atribuição destrutiva, pois “destrói” o valor anterior (10) que estava associado à variável x.
O leitor atento pode imaginar que o x = 10 também altera o estado do programa, pois cria uma variável que não existia antes. Mas isso não é verdade, de um ponto de vista abstrato, matemático. Considere esta instrução como um simples binding: uma associação nome?valor. Imagine um heap infinito, onde todos os objetos possíveis existem o tempo todo: por exemplo, todos os 232 inteiros possíveis, todas as Strings com todas as infinitas combinações de caracteres, etc. Neste modelo teórico, a única coisa que um binding faz é dar nome aos bois; associar a certo objeto um identificador que permite referenciá-lo. É claro que no mundo real da implementação de qualquer linguagem, essa abstração é quebrada, pois qualquer valor tem que ser alocado na memória e inicializado num determinado instante, e pode deixar de existir no instante seguinte. Porém, isso é apenas um detalhe de implementação.
Para a questão de modularidade, somente as atribuições destrutivas são realmente danosas, vejamos o porquê. Quando você pensa na interface pública de um método ou objeto, parece que esta é definida somente pelos seus parâmetros, tipo de retorno, e no caso do Java suas exceções. Mas na verdade, o método pode ter também dependências do estado do programa. E pode modificar esse estado, alterando atributos de objetos. Assim, a interface real dos seus métodos e objetos acaba sendo bem maior do que parece, o que aumenta o acoplamento do código e reduz a modularidade.
Este é um post disponível para assinantes MVPou para quem possui Créditos DevMedia. Clique aqui para saber mais!
Osvaldo Pinali Doederlein
é Mestre em Engenharia de Software Orientado a Objetos e Arquiteto de Tecnologia da Visionnaire Informática, trabalhando em projetos de software e prospecção tecnológica.



