O nascimento
Erlang nasceu no laboratório de ciência da computação da Ericsson, na década de 1980, influenciada por linguagens como ML, Ada, Module, Prolog e Smalltalk, quando um time de três cientistas – entre eles, Joe Armstrong, considerado o pai da linguagem – receberam a missão de descobrir qual seria a melhor linguagem de programação para escrever a próxima geração de sistemas de telecom. Após dois anos de ardua investigação e prototipação, estes cientistas descobriram que nenhuma linguagem era completa o bastante para tal objetivo e, conclusivamente, decidiram criar uma nova linguagem. Nasceu então Erlang, the ERicsson LANGuage.
De lá pra cá, Erlang vem sendo evoluída e usada para escrever grandes sistemas críticos, porque é exatamente nesse cenário que Erlang mostra melhor seu valor.
O primeiro grande caso de sucesso foi do AXD301 ATM Switch, que tem em seu coração mais de 1,5 milhão de linhas de código Erlang lidando com toda a complexidade de controle lógico, supervisão de operação e manteneção, integrado a mais meio milhão de linhas de código C/C++, que lida com protocolos de baixo nível, drivers, etc. Este switch foi usado pela British Telecom em 2002 com a inacreditável marca de 99,9999999% de disponibilidade, segundo um press release da própria Ericsson. À época, Bernt Nilson, diretor do programa Ericsson Next Generation Systems, confirmou que "a rede tem estado tão disponível, mas tão disponível, que existe o risco de nossos engenheiros de campo não desenvolverem habilidades de manutenção".
Diferente de algumas linguagens que nascem para encontrar um nicho, Erlang nasceu com um problema a ser resolvido, com requisitos a serem atendidos. Tolerância a falhas, concorrência realmente pesada, computação distribuída, atualização da aplicação sem derrubá-la, sistemas de tempo real, este é o nicho de Erlang, foi para isto que Erlang nasceu.
- Facebook, no backend de seu sistema de chat, lidando com 100 milhõs de usuários ativos;
- Delicious, que tem mais de 5 milhões de usuários e mais de 150 milhões de bookmarks;
- Amazon SimpleDB, o serviço de dados do poderoso Amazon EC2;
- GitHub, no seu sistema de backend, lidando com milhares de transações concorrentes;
- Motorola, CouchDB, RabbitMQ, Ejabbed, entre outros.
1 - Expressividade e beleza
Há quem diga que não, mas Erlang é uma linguagem muito bonita. Dado as já citadas influências de Erlang, ela é uma linguagem bem expressiva e declarativa, que encoraja a escrita de código simples e objetivo, o que certamente promove código fácil de ler, escrever e organizar [em módulos reutilizáveis]. O snippet abaixo é um exemplo de implementação do famoso fatorial:
-module(sample1).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N-1).
2 - Forte uso de recursividade
Esta certamente é uma clara herança da veia funcional de Erlang, que torna o código muito menos imperativo e mais declarativo. Mas além da beleza declarativa óbvia, é importante saber que Erlang foi projetada para lidar com iterações recursivas gigantescas sem qualquer degradação de performance – e sem estourar o “heap” de memória. O item anterior é um bom exemplo disso.
3- Livre de efeito colateral
Uma das coisas que Erlang favorece é a construção de sistemas altamente concorrentes rodando em processadores com multiplos núcleos. E como concorrência e efeito colateral absolutamente não combinam, em Erlang, uma vez que um dado valor tenha sido atribuído a uma variável, esta não pode mais ser modificada – ou seja, estão mais para o que conhecemos por constantes do que para variaveis.
Se você já escreveu código concorrente sabe o quanto tê-lo livre de efeitos colaterais te faz livre de dores de cabeça. Poucas coisas são tão deprimentes quanto debugar código concorrente repleto de efeitos colaterais.
4- Pattern matching
Pattern matching em Erlang é usado para associar valores a variáveis, controlar fluxo de execução de programas, extrair valores de estruturas de dados compostas e lidar com argumentos de funções. Mais um ponto para código declarativo e expressividade. Como mostra o código abaixo:
-module(sample2).
-export([convert_length/1]).
convert_length({_, 0}) ->
{error, unconvertable_value},
convert_length(Length) ->
case Length of
{centimeter, X} ->
{inch, X / 2.54};
{inch, Y} ->
{centimeter, Y * 2.54}
end.
Fala por si, não fala? Pattern matching para selecionar a função – o que acaba validando a entrada – e para conversão da unidade de medida.
5- Concorrência baseada em passagem de mensagens (a.k.a. Actors) Acho que concorrência baseada em passagem de mensagem entre atores é uma das features mais populares de Erlang. Vejamos o porque com o famoso exemplo do Ping-Pong:
-module(sample3).
-export([start/0, ping/2, pong/0]).
ping(0, Pong_PID) ->
Pong_PID ! finished,
io:format("Ping finished~n", []);
ping(N, Pong_PID) ->
Pong_PID ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping(N - 1, Pong_PID).
pong() ->
receive
finished ->
io:format("Pong finished~n", []);
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong()
end.
start() ->
Pong_PID = spawn(sample3, pong, []),
spawn(sample3, ping, [3, Pong_PID]).
Neste pequeno snippet podemos observar algumas características de Erlang já citadas neste post, tal como pattern matching na captura das mensagens e recursividade no controle das iterações. Agora, falando do aspecto concorrente em si, algumas coisas são particularmente interessantes aqui:
- Em Erlang, a concorrência acontece entre processos leves, diferente de linguagens como C++ e Java, que baseiam sua concorrência em threads nativas de sistema operacional, que são caríssimas;
- Em Erlang, há um tipo de dado chamado PID, o qual é o identificador do processo paralelo (mais conhecido como Actor) e para o qual as mensagens podem ser enviadas.
Releia o código acima com estas informações em mente e veja como concorrência em Erlang é algo completamente descomplicado e natural. Depois, pense no mesmo código implementado em C#, Java ou C++. Não, não, brincadeira, não pense não
Conclusão