Programação Funcional para Desenvolvedores Java
Dean Wampler
Novatec
Authorized Portuguese translation of the English edition of titled Functional Programming for Java Developers, First Edition ISBN 9781449311032 © 2011 Dean Wampler. This translation is published and sold by permission of O'Reilly Media, Inc., the owner of all rights to publish and sell the same. Tradução em português autorizada da edição em inglês da obra Functional Programming for Java Developers, First Edition ISBN 9781449311032 © 2011 Dean Wampler. Esta tradução é publicada e vendida com a permissão da O'Reilly Media, Inc., detentora de todos os direitos para publicação e venda desta obra. © Novatec Editora Ltda. 2012. Todos os direitos reservados e protegidos pela Lei 9.610 de 19/02/1998. É proibida a reprodução desta obra, mesmo parcial, por qualquer processo, sem prévia autorização, por escrito, do autor e da Editora. Editor: Rubens Prates Tradução: Acauan Pereira Fernandes Revisão gramatical: Giacomo Leone Neto Editoração eletrônica: Carolina Kuwabata ISBN: 978-85-7522-316-1 Histórico de impressões: Setembro/2012
Primeira edição
Novatec Editora Ltda. Rua Luís Antônio dos Santos 110 02460-000 – São Paulo, SP – Brasil Tel.: +55 11 2959-6529 Fax: +55 11 2950-8869 E-mail: novatec@novatec.com.br Site: www.novatec.com.br Twitter: twitter.com/novateceditora Facebook: facebook.com/novatec LinkedIn: linkedin.com/in/novatec
Dados
Internacionais de Catalogação na Publicação (Câmara Brasileira do Livro, SP, Brasil) Wampler, Dean Programação funcional para desenvolvedores Java / Dean Wampler ; [tradução Acauan Pereira Fernandes]. -- São Paulo : Novatec Editora, 2012. Título original: Functional programming for Java developers Bibliografia ISBN 978-85-7522-316-1 1. Java (Linguagem de programação para computador) I. Título.
12-11198
CDD-005.133 Índices para catálogo sistemático: 1. Java : Linguagem de programação : Computadores : Processamento de dados 005.133 MP20120904
(CIP)
capítulo 1
Por que programação funcional?
Há alguns anos, quando muitos desenvolvedores começavam a falar sobre programação funcional (FP – Functional Programming) como a melhor forma de lidar com o tópico de concorrência, decidi que estava na hora de aprender mais e a julgar por mim mesmo. Esperava aprender algumas ideias novas, mas supus que ainda usaria a programação orientada a objetos (OOP) como abordagem principal ao desenvolvimento de software. Estava errado. À medida que aprendia sobre programação funcional, encontrava boas ideias para implementar concorrência, conforme havia imaginado, mas também descobri que ela clareava meu pensamento sobre o projeto de tipos1 e funções. Ela também me permitia escrever código mais conciso. A programação funcional me fez repensar onde os limites dos módulos deveriam ficar e como torná-los melhor para a reutilização. Descobri que a comunidade de programação funcional estava criando sistemas de tipos mais poderosos e inovadores que ajudam a garantir a correção. Também concluí que a programação funcional é mais apropriada para muitos dos desafios únicos da nossa época, como trabalhar com conjuntos massivos de dados e mesmo assim não perder a agilidade já que os requisitos mudam cada vez mais rapidamente e os cronogramas ficam cada vez mais curtos. Em vez de continuar sendo um desenvolvedor OOP que atira um pouco de FP para temperar, atualmente escrevo programas que usam objetos criteriosamente. Você poderia dizer que comecei na FP por causa do tópico da concorrência, mas permaneci pela “mudança de paradigma”. 1 Ocasionalmente usarei tipo e classe de modo intercambiável, mas não são sinônimos. Veja as definições no Glossário.
13
14
Programação Funcional para Desenvolvedores Java
O engraçado é que já passei por isso antes. Um fenômeno muito semelhante ocorreu na década de 1980 quando a OOP começou a ser amplamente usada. Os objetos são uma forma ideal de representação gráfica dos widgets, de modo que a OOP era uma escolha natural para desenvolver interfaces gráficas de usuário (GUIs – Graphical User Interfaces). Todavia, assim que as pessoas começavam a usar objetos, achavam que eles eram uma forma intuitiva de representar muitos “domínios”. Você poderia modelar o problema em objetos e então colocar o mesmo modelo de objetos no código! Até mesmo detalhes de implementação, como diversas formas de entrada e saída, pareciam ideias para a modelagem em objetos. Mas sejamos claros: tanto a FP quanto a OOP são ferramentas, não panaceias. Cada uma tem vantagens e desvantagens. É fácil ficar com a testada e provada, mesmo quando pode haver uma forma melhor disponível. Mesmo assim, é difícil acreditar que objetos, que trabalharam tão bem no passado, pudessem ser menos valiosos atualmente, não é? Para mim, meu crescente interesse na programação funcional não é um repúdio aos objetos, que se provaram benéficos. Em vez disso, é um reconhecimento de que as desvantagens dos objetos são mais difíceis de ignorar quando deparadas com os desafios de programação atuais. Os tempos são diferentes da época em que os objetos estavam em ascensão há diversas décadas. Aqui, resumidamente, está o motivo pelo qual me tornei um programador funcional e por que acredito que você deveria aprendê-la também. Para mim, a programação funcional oferece a melhor abordagem para satisfazer aos requisitos a seguir, com os quais me deparo diariamente.
Tenho que ser bom na escrita de programas concorrentes Antigamente alguns dos “caras inteligentes” da equipe escreviam a maior parte do código concorrente, usando concorrência multithread, que requer acesso cuidadosamente sincronizado a estado compartilhado mutável. Ocasionalmente, todos recebiam uma ligação à meia-noite para depurar algum problema sério de concorrência que aparecia após
Capítulo 1 ■ Por que programação funcional?
15
o programa já estar sendo usado em produção. Mas, na maior parte do tempo, a maioria dos desenvolvedores poderia ignorar a questão de concorrência. Atualmente, até mesmo o seu telefone tem diversos núcleos de CPU (ou o seu próximo terá). Aprender a escrever software com concorrência robusta não é mais algo opcional. Felizmente, a programação funcional lhe apresenta os princípios corretos para pensar em concorrência e difundiu diversas abstrações de concorrência de nível mais alto que tornam a tarefa muito mais fácil. A programação multithreads, que necessita de acesso sincronizado a estados compartilhados que podem ser alterados, é a linguagem assembly da concorrência.
A maioria dos programas são apenas problemas de gerência de dados Atualmente trabalho muito com grandes volumes de dados, na sua maior parte usando o ecossistema de ferramentas Apache Hadoop, construído em torno do MapReduce [Hadoop]. Quando está recebendo terabytes de novos dados por dia, precisa limpar e armazenar esses dados e depois executar uma análise nos petabytes de dados acumulados, você simplesmente não pode se permitir o overhead de objetos. Você quer estruturas de dados e operações muito eficientes sobre esses dados, com um overhead mínimo. A antiga frase da metodologia ágil que diz “Qual a coisa mais simples que pode funcionar?” ganha um novo significado. Comecei pensando em como gerenciamos conjuntos de dados menores, digamos uma aplicação típica de TI com um banco de dados. Se os objetos adicionam overhead em problemas com grandes volumes de dados, também o fazem para problemas com pequenos volumes de dados? É menos provável que o desempenho e o tamanho do armazenamento sejam um problema nesse caso, mas a agilidade da equipe é uma questão universal. Como uma equipe pequena permanece ágil ao melhorar uma aplicação de TI, ano após ano? Como a equipe mantém o código base tão conciso quanto possível?
16
Programação Funcional para Desenvolvedores Java
Fui levado à conclusão de que representar fielmente o modelo de objetos do domínio no código pode ser questionável. O mapeamento objeto-relacional (ORM – Object-Relational Mapping) e formas semelhantes middleware para objetos adicionam overhead para transformar dados relacionais em objetos, mover esses objetos pela aplicação e depois transformá-los de volta em dados relacionais para atualizações. É claro que todo esse código extra tem de ser testado e mantido. Sei que esta prática surgiu em parte porque adoramos objetos e muitas vezes odiamos dados relacionais, ou talvez apenas odiemos trabalhar com bancos de dados relacionais. (Falo da minha experiência pessoal.) Todavia, dados relacionais, como os conjuntos de resultados de consultas, são realmente apenas coleções que podem ser manipuladas de uma forma funcional. Seria melhor trabalhar diretamente com esses dados? Mostrarei a você como trabalhar diretamente com coleções mais fundamentais de dados minimiza o overhead de trabalhar com modelos de objetos, ao mesmo tempo em que evita a duplicação e promove o reuso.
A programação funcional é mais modular Anos atrás, eu tinha um cliente grande que lutava para realizar seu trabalho com seu código base inchado. Seus competidores estavam obtendo enormes vantagens comparados com ela. Um dia eu vi algo que resumia seu problema. Uma parede com um diagrama UML de mais de um metro e sessenta. Lembro-me de uma classe em especial, a classe Customer (Cliente). Ela se estendia por todo o metro e sessenta. Isso era uma falha na modularidade, especificamente em encontrar os níveis corretos de abstração e decomposição. A classe Customer havia se tornado um saco de tudo que alguém pudesse associar a um de seus clientes. No fim da década de 1980, quando a programação orientada a objetos estava no seu auge, muitas pessoas esperavam que os objetos finalmente resolveriam o problema de criar componentes reusáveis que você conectasse para criar aplicativos, reduzindo grandemente o custo e o tempo de desenvolvimento. Essa visão parece tão razoável que é fácil não perceber o fato de que não acabou tão bem quanto o esperado.
Capítulo 1 ■ Por que programação funcional?
17
A maioria dos exemplos bem sucedidos de bibliotecas reusáveis são plataformas que definiram seus próprios padrões que todos tiveram que seguir. Exemplos incluem o JDK, o Spring Framework e a API de plugin Eclipse. Até mesmo a maioria das “bibliotecas de componentes” de terceiros que poderíamos usar (por exemplo a Apache Commons) têm suas próprias APIs personalizadas com as quais devemos ficar em conformidade. Quanto ao resto do código que precisamos, ainda reescrevemos muito dele projeto após projeto. Assim, o desenvolvimento de software orientado a objetos não é o “assembly dos componentes” que esperávamos que surgisse. A flexibilidade quase sem limite dos objetos na verdade mina o potencial de reuso, porque há poucos padrões sobre como os objetos devem se interconectar e não conseguimos chegar a um acordo nem sobre nomes básicos para as coisas! Sistemas com restrições maiores são na verdade mais modulares, o que é um paradoxo. O livro Design Rules: The Power of Modularity [Baldwin2000] demonstra que o crescimento explosivo da indústria de PCs foi possibilitado quando a IBM criou o padrão de fato para a arquitetura de hardware de computador pessoal. Por causa dos barramentos padronizados para periféricos e conectores, permitiu aos inovadores criar drives, mouses, monitores, placas-mãe etc., novos, melhores e mais rápidos. A eletrônica digital é por si mesma um ótimo exemplo de um sistema modular. Cada fio carrega apenas um sinal 0 ou 1; mesmo quando você os junta em grupo de 8 16, 32 e 62, pode criar camadas de protocolos que possibilitam todas as coisas maravilhosas que conseguimos fazer com os computadores. Não há padrões semelhantes para componentes baseados em objetos. Diversas
tentativas como CORBA e COM tiveram sucesso modesto, mas acabaram falhando pelos mesmos motivos fundamentais, o de que os objetos estão no nível errado de abstração. Conceitos como “cliente” raramente são novos, mas mesmo assim não conseguimos parar de inventar uma nova representação para eles a cada novo projeto, porque cada projeto novo traz o seu próprio contexto e requisitos. Entretanto, se percebermos que um objeto é fundamentalmente apenas um agregado de dados, então podemos ver uma forma de definir abstrações melhor padronizadas em níveis mais baixos do que objetos, análogas
18
Programação Funcional para Desenvolvedores Java
a circuitos digitais. Esses padrões são coleções básicas como lista, mapa e conjunto, junto com tipos “primitivos” como números e poucos conceitos de domínios bem definidos (e.g., Money em um aplicativo financeiro). Uma ajuda maior à modularidade é a natureza das funções na programação funcional, a qual evita efeitos colaterais, tornando-as livres de dependências de outros objetos e portanto mais fáceis de reusar em muitos contextos. O resultado é que um programa funcional define abstrações onde elas são mais úteis e mais fáceis de reusar, integrar e também de testar. Qualquer objeto arbitrariamente complexo pode ser decomposto em valores atômicos (como primitivas) e coleções contendo esses valores e outras coleções.
Tenho que trabalhar cada vez mais rapidamente Os ciclos de desenvolvimento estão indo assintoticamente para duração zero. Isso parece loucura, especialmente se você iniciou a programação profissional na mesma época que eu, quando os projetos geralmente duravam meses, até mesmo, anos. Entretanto, atualmente, há muitos sites na internet que instalam código novo diversas vezes por dia e todos nós estamos sentindo a pressão de executar o trabalho mais rapidamente, mas sem sacrificar a qualidade, é claro. Quando os cronogramas eram mais longos, fazia mais sentido modelar seu domínio cuidadosamente e implementá-lo em código. Se você cometesse um erro, levaria meses para corrigi-lo com uma nova versão. Atualmente, na maioria dos projetos, entender o domínio com precisão é menos importante do que entregar alguma coisa valiosa rapidamente. Nossa compreensão do domínio mudará rapidamente, de qualquer forma, à medida que nós e nossos clientes descobrimos mais coisas a cada instalação. Se entendermos mal algum aspecto do domínio, podemos consertar esses erros rapidamente quando fazemos instalações frequentes.
Capítulo 1 ■ Por que programação funcional?
19
Se a modelagem cuidados parece menos importante, a implementação conscienciosa do modelo de objetos é ainda mais suspeita atualmente do que no passado. Embora o Agile Software Development (Desenvolvimento Ágil de Software) tenha melhorado bastante nossa qualidade e capacidade de responder a mudanças, precisamos repensar formas de manter nosso código “minimamente suficiente” para os requisitos atuais, mas ainda assim flexível a mudanças. A programação funcional nos ajuda a fazer isso.
A programação funcional é um retorno à simplicidade Finalmente, construindo sobre os pontos anteriores, vejo a programação funcional como uma reação à complexidade acidental, o tipo que nós mesmos adicionamos pelas nossas escolhas de implementação, ao contrário da complexidade inerente ao domínio do problema2. Assim, por exemplo, muito do middleware orientado a objetos nos nossos aplicativos atualmente é desnecessário e desperdiçador, na minha opinião. Sei que algumas dessas alegações são provocativas. Não estou tentando convencer você a abandonar totalmente os objetos ou se tornar um fanático da FP. Estou tentando lhe apresentar uma caixa de ferramentas maior e uma perspectiva mais ampla, de modo que possa tomar decisões de projeto mais bem informado e talvez refrescar seu entusiasmo pela arte e ciência do desenvolvimento de software. Espero que esta breve introdução lhe mostre o motivo pelo qual meu pensamento mudou. Talvez o seu mude também. Vamos começar!
2 Não quero dizer que a programação funcional seja simples. Tornar-se um especialista em programação funcional requer o domínio de muitos conceitos avançados e poderosos.