Por que eu programo em Erlang

Eu estava pesquisando sobre Erlang, especialmente sobre sites em português, e, no meio da busca (que retornou, basicamente, vários sites em inglês), encontrei este excelente artigo (de 2012) do Evan Miller: Why I Program In Erlang.

Depois de lê-lo, logo me veio à mente: este é o conteúdo perfeito para o primeiro post do “Erlang de Hoje”! (Site que, agora, está morto e será migrado para cá.) Então, depois de pedir permissão ao Evan, logo comecei a traduzi-lo.

Lembrando que é uma tradução livre e não-oficial, segue o conteúdo:

Por que eu programo em Erlang

Por Evan Miller

20 de outubro de 2012

Erlang é uma linguagem com mais de 25 anos de vida que nunca ganhou um concurso de popularidade e quase certamente nunca ganhará qualquer medalha por velocidade, nem sequer uma coroa de louros por beleza sintática. A linguagem é lenta, esquisita e feia. Refatoração de código Erlang é um sofrimento.

Ainda assim, por quase cinco anos, eu tenho gastado uma boa parte do meu tempo livre programando em Erlang. Até agora, gastei bem mais que mil horas com a linguagem. Tenho usado-a para escrever, em ordem mais ou menos cronológica, um parser de CSV (não ria, eu disse “ordem cronológica”), um compilador de templates, um mapeador objeto-relacional, um parser de rich-text, um redimensionador de imagens, um pré-processador de linguagem, um framework web e uma fila de mensagens distribuída. O que se segue são minhas impressões sobre a linguagem quando comparada a outras linguagens que eu já usei profissionalmente (C, Java, Perl, PHP, Ruby, Objective-C, and JavaScript).

A boa notícia sobre Erlang pode ser resumida a: Erlang é o resultado de vinte e cinco anos de decisões de design corretas na linguagem e na plataforma. Toda vez que me pergunto como alguma coisa funciona em Erlang, eu nunca fico desapontado com a resposta. Quase sempre fico com a impressão de que os designers fizeram “a coisa certa”. Me parece que isso contrasta com Java, que faz a coisa mais pedante, com Perl, que faz a coisa mais esquisita, com Ruby, que tem duas implementações diferentes para a coisa errada e C, que simplesmente não faz nada.

Peguemos garbage collection como exemplo. Quando é a hora de recolher o lixo em outras linguagens, o sistema inteiro tem que parar enquanto o garbage collector é executado. Essa abordagem é perfeitamente ok se o seu programa apenas roda uma vez, escreve algo e então termina. Mas em aplicações com execução longa, como desktop, mobile ou programas de servidor, essa estratégia resulta, ocasionalmente, em interfaces de usuário congeladas e tempos de resposta lentos. Programas Erlang, por outro lado, podem ter milhares de heaps independentes que tem suas coletas de lixo feitas separadamente. Dessa forma, a penalidade no desempenho é diluída pelo tempo, e então sua aplicação não parará de responder misteriosamente de tempos em tempos enquanto o garbage collector é executado.

Ou vejamos a concatenação de strings. Se você buscar pela implementação de concatenação de strings em Perl, Ruby ou Javascript, certamente encontrará um if, um realloc e um memcpy. Ou seja: quando você concatena duas strings, a primeira “cresce” de maneira a ter espaço para a adição da segunda, e então a segunda é copiada “para dentro” da primeira. Essa abordagem funcionou por décadas e é a coisa “óbvia” a fazer. Já a abordagem da Erlang é não-óbiva e, eu acredito, correta. No caso comum, Erlang não usa um pedaço contíguo de memória para representar uma sequencia de bytes. Ao invés disso, usa algo chamado “I/O list”, que é uma lista aninhada de pedaços não-contíguos de memória. O resultado é que a concatenação de duas strings ocorre em O(1) em Erlang, comparado com O(N) em outras linguagens. É por isso que renderizar um template em Ruby, Python, etc. é algo lento, mas em Erlang é muito rápido.

Não importa o quão “bloqueante” ou concorrente a lógica da sua aplicação é, é impossível fazer uma chamada de rede bloqueante em Erlang, ou iniciar múltiplos processos no sistema operacional. Essa decisão de design faz com que um servidor Erlang nunca derrube o sistema operacional. Tendo perdido muitas noites de sono devido a sistemas operacionais sobrecarregados em trabalhos anteriores, eu acredito que o design concorrente da Erlang é correto.

Eu cheguei a mencionar que refatoração de código em Erlang é um sofrimento. Felizmente eu raramente precisei refatorar código Erlang da mesma forma que código orientado a objetos precisa de refatoração de tempos em tempos. Em Erlang, passa-se para cada função toda a informação que ela precisa, e você ganha um warning do compilador se for passada informação que a função não precisa. De certa forma, refatoração é integrada ao desenvolvimento, não uma atividade distinta que requer cuidadoso test coverage e várias xícaras de café. Refatorar código Java ou Objective-C geralmente torna-se necessário porque as classes tem métodos demais e o desenvolvedor precisa gastar tempo para descobrir quais métodos requerem quais variáveis e como cortar melhor a carroagem ao meio. Em programação funcional, isso não é uma preocupação. Mover uma função para um módulo diferente requer pouquíssimo esforço. “Refatoração” em Erlang consiste em quebrar funções grandes em mais funções menores. Não há muito esforço mental envolvido. Porém, devido às peculiaridades sintáticas de Erlang, pode ser bem chato converter funções anônimas para funções com nome. Talvez IDEs espertas, um dia, eliminarão esse tédio todo.

Todas as estruturas de dados em Erlang são completamente transparentes. Mesmo sabendo-se nada sobre a biblioteca que vocẽ está usando, é sempre possível inspecionar o conteudo de estruturas de dados em tempo de execução. Essa característica ajuda muito na depuração e é uma benção para o hacking das antigas. É fácil manipular estruturas de dados não documentadas para que se possa implementar funcionalidades que o autor original da biblioteca não pretendia. Ao contrário da programação orientada a objetos, você nunca precisará se preocupar com o autor original renomeando variáveis e quebrando o código da sua sub-classe. Contanto que a estrutura de dados base continue a mesma, suas modificações continuarão a funcionar em Erlang.

Percebi que a transparência das estruturas de dados de Erlang tornam a programação muito mais fácil. Em programação orientada a objetos, sempre estou me preocupando sobre como dar nomes às coisas. Em Erlang, isso geralmente não importa, já que a estrutura de dados é metade da interface. Se você nunca programou em Erlang, você provavelmente não faz ideia do que estou falando.

E agora vem as más notícias sobre Erlang: os benefícios da linguagem só se mostram no futuro, ou seja, a maioria deles só podem ser apreciados depois de muitos anos usando-se outras linguagens seguido de muitos anos usando-se Erlang. Ela certamente não é uma linguagem para iniciantes. A sintaxe é estranha para programadores vindo da “diáspora do C”. Programação funcional é difícil, e Erlang não facilita as coisas em nada. Os toolkits gráficos são primitivos e não há jogos de preencha-o-código como encontrados em cursos introdutórios de Java. Ler qualquer código não-trivial em Erlang requer um entendimento firme de recursão, um tipo de abstração que muitas pessoas consideram difícil.

Erlang também tem poucas bibliotecas se comparada a outras linguagens. Na minha experiância, para qualquer tarefa, há zero, uma ou no máximo duas bibliotecas Erlang disponíveis. Talvez eu esteja sozinho quando digo isso, mas eu gosto, no fim das contas, do fato de não haver muitas bibliotecas Erlang disponíveis. Se eu preciso de alguma coisa, eu tenho uma desculpa para fazer isso por conta própria e, às vezes, eu descubro coisas que não teria descoberto de outra forma. Parece besta, mas é verdade. Eu posso me sentir produtivo porque estou fazendo algo que ninguém fez antes, e no meio do caminho eu tenho a liberdade de tentar abordagens novas e fazer inovações reais. Eu aprendi mais enquanto desenvolvia bibliotecas em Erlang do que enquanto amarrava código C ou Ruby de terceiros. Eu programo em Erlang puramente pelo prazer de resolver problemas e compartilhar minhas descobertas em aplicações bem desenhadas.

Para um programador hobbista experiente como eu, a única má notícia real sobre Erlang é que ela é lenta. Para as aplicações de servidor que eu tenho escrito, a velocidade da linguagem não tem sido um problema. O custo extra em CPU foi mais que superado pelo gerenciamento correto da coleta de lixo, I/O de rede e concatenação de strings em um ambiente concorrente. Na análise da complexidade da linguagem, programas em Erlang tendem a ter um custo alto já de início, mas propriedades assintóticas excelentes.

Para o programador que quer escrever programas rápidos usando Erlang — o tipo de programa que inicia, executa, escreve algo e termina — há esperança em vários fronts. Há um compilador de código nativo disponível e, de acordo com benchmarks numéricos, ele torna Erlang mais rápida que Ruby, Perl e PHP, apesar de mais lenta que Java e Javascript. Há boatos de um compilador just-in-time, que pode prover mais melhorias ao tempo de execução ao pegar informações da própria execução do código e fazer otimizações apropriadas. E, finalmente, almas corajosas podem escrever código computacionalmente intensivo usando C via NIF – e é importante lembrar que o código C vai bloquear o scheduler da Erlang (potencialmente negando suas capacidades de concorrencia) e fazer código de execução paralela em C é notadamente difícil.

Minha própria escolha para escrever programas rápidos em Erlang é uma tecnologia alternativa que contém todos os benefícios do C sem comprometer a integridade do runtime da Erlang. Trata-se da OpenCL, uma linguagem C-like criada pela Apple em 2008. Assim como Erlang, OpenCL faz uso fácil dos vários cores da CPU da máquina; mas, ao contrário de Erlang, os programas são muito rápidos. De fato, programas em OpenCL costumam ser mais rápidos que programas em C, já que eles fazem uso melhor dos vários núcleos. Programas OpenCL podem ser compilados e executados diretamente a partir de código Erlang. A meu ver, é a tecnologia perfeita para se executar tarefas de computação computacionalmente intensiva (isso é, executando aqueles loops internos) dentro de um programa Erlang.

É importante eu dizer que nunca usei, de fato, OpenCL dentro de um programa Erlang. Como já disse, velocidade não tem sido um problema nos programas Erlang que tenho escrito. Mas, sim, eu tenho experiência com OpenCL e tenho ficado bastante satisfeito. Eu escrevi uma biblioteca de projeção de mapas em OpenCL que é cerca de 5 vezes mais rápida que a biblioteca “state-of-the-art” Proj.4 (escrita em C). Também escrevi uma biblioteca para fazer estatísticas multiderivativas. Ainda não fiz benchmarks, mas suspeito que seja mais rápida por uma margem similar. Há algumas peculiaridades ao escrever-se em OpenCL, mas eu espero que um dia todos os loops mais “apertados” do mundo sejam escritos em OpenCL e chamados a partir de grandes programas escritos em Erlang.


Este artigo foi escrito originalmente em 01/12/2014.