You are currently browsing the archives for the erlang category


A briefly introduction to Cameron

Hi everybody!

I’ve started to write a kind of documentation about Cameron, in the spirit of a presentation, really brief and direct. It’s still a work in progress, like Cameron as well, but I think it’s already able to give you an idea on what Cameron aim to be.

Stay tuned…

A briefly introduction to Cameron

Who is Cameron?

Cameron is an Erlang-based workflow engine in which I have been working on for a few weeks.

It has been built as an Erlang/OTP system with an REST-like API, powered by Misultin, through which one can POST a JSON request to run a given process workflows, that will be executed in background [parallely with other running ones], and then GET its JSON results as soon as them become available. And obviously, it uses Redis for the win.

What about Process Workflows?

Process workflows are defined in terms of REST-like web services, written in virtually any language, which basically must talk a simple JSON contract.

These web services are the activities that define a process workflow; these web services are the tasks to achieve a given target. And as you can imagine, yes, an activity can cascade many others; it is pipeline-based, as well.

So if you have any background job that must cascade many tasks to achieve a goal, maybe it fits to your needs.

Does it work?

Although it still experimental, a work in progress, it works reasonably well – at least under my tests.

So if you have time, take a look at the documentation.

Erlang: system_info pode te dizer coisas legais

Ontem à noite, antes da sessão pão de queijo com chá mate aqui em casa, resolvi brincar um pouco com uns módulos de baixo nível de Erlang, porque de manhã havia me lembrado de um post bem legal, de um cara chamado Krzysztof Kliś, que li no ano passado. (Aliás, aproveitei e colei o código dele no meu gist, pra favorecer a leitura com syntax highlighting.)

Bem, dessa brincadeira saiu o módulo system_info, que você pode conferir no meu GitHub, no projeto de estudo getting_system_info.

O que ele faz?

Basicamente, ele oferece informações de load da máquina, quantidade de processos rodando e uso de memória do nó atual ou do cluster. Você pode ter mais detalhes sobre o formato dessas informações dando uma olhada nos testes do projeto.

Qual a utilidade disso?

Como disse, escrevi esse código depois de me lembrar do post do Krzysztof Kliś sobre seu load balancer [de apenas 48 linha]. Escrevi por pura diversão.

Mas, sim, se amanhã ou depois eu precisar escrever algum load balancer ou aplicativo que monitore load, processos e memória do meu cluster, ele poderá me servir de helper.

Dá pra testar e ver se funciona?

Sim, a menos que você seja usuário de Windows. Não, não é preconceito, é que algumas funções que uso não estão disponíveis pra Windows, apenas pra sistemas operacionais Unix-like.

Você pode fazer isso de duas maneiras:

1- Rodando os testes

Clone o projeto – o único problema é que você vai levar uma porção de outros projetos de brinde. I’m sorry! – e em seu diretório raíz, compile e rode os testes.

$ ./compile.sh
$ ./test.sh

Só que dessa maneira você não consegue ver nenhuma informação útil. Vamos à segunda maneira.

2- Rodando as funções no Eshell

Depois de devidamente clonado e compilado, crie um cluster com três nós. (Se não souber como a arquitetura de cluster de runtimes Erlang funciona, tenho um post introdutório pra você.)

Na raíz do projeto:

a. Inicie três nós, um em cada terminal

$ erl -sname n1 -pa ebin/
$ erl -sname n2 -pa ebin/
$ erl -sname n3 -pa ebin/

b. No n1, estabeleça conexão entre os nós

(n1@codezone)1> net_kernel:connect('n2@codezone').
(n1@codezone)2> net_kernel:connect('n3@codezone').

Agora, todos os três nós estão conectados uns aos outros, já que, por padrão, os nós compartilham suas conexões uns com os outros.

c. Ainda no n1, verifique as informações de system_info

(n1@codezone)3> system_info:node_load().
(n1@codezone)4> system_info:cluster_load().
(n1@codezone)5> system_info:node_processes().
(n1@codezone)6> system_info:cluster_processes().
(n1@codezone)7> system_info:node_memory().
(n1@codezone)8> system_info:cluster_memory().

Agora, sim, você tem informações a valer!

Gostou?

Espero que sim, porque system_info pode te dizer coisas legais. =)

Erlang: Programação Distribuída de maneira simples

Fazia tempo que estava para blogar algo sobre como é fácil fazer programação distribuída com Erlang. Derrepente, hoje, acabei perdendo o sono e eis aqui o post.

O mais simples do mundo

Pra começar, vamos ver o exemplo mais simples de computação distribuída que se pode fazer e aprender alguma coisa com ele. Ele é como um simples chat. Pois muito bem, vejamos!

Fazendo o setup

No terminal, inicie um Eshell com o seguinte comando:

$ erl -sname jose

E num outro terminal, inicie outro Eshell com:

$ erl -sname maria

Agora vá no primeiro terminal, no Eshell do José, e execute o seguinte comando:

(jose@codezone)1> register(console_jose, self()).

E no segundo terminal, no Eshell da Maria, faça o mesmo:

(maria@codezone)1> register(console_maria, self()).

Tudo bem, setup feito, hora do bate-papo!

Jogando conversa fora

“Ei José, diga oi…”

(jose@codezone)2> {console_maria, 'maria@codezone'} ! "Oi Maria, tudo bem?".

Veja o terminal da Maria. O que aconteceu? Nada? A mensagem não chegou? Chegou, chegou sim. Vejamos:

(maria@codezone)2> regs().

Na primeira coluna, procure pelo processo console_maria. Achou? Agora veja na última coluna desse processo. Que número tem aí? 1? Pois é, isso nos indica uma coisa. Ainda nesse Eshell, execute o seguinte comando:

(maria@codezone)3> flush().

Opa! E agora? Shell got “Oi Maria, tudo bem?”, certo? Taí a mensagem que a Maria recebeu do José.

Agora, que tal a Maria ser gentil com o José e responde-lo cordialmente?

(maria@codezone)4> {console_jose, 'jose@codezone'} ! "Vou bem, e voce?".

Se você quiser, pode ver o registro de processos no Eshell do José, como fez anteriormente com o da Maria. Ou pode simplesmente:

(jose@codezone)3> flush().

E receber a mensagem Shell got “Vou bem, e voce?”.

Entendendo tudo isso

Por sorte, não há muito o que explicar até aqui – já que fizemos o exemplo mais simples do mundo. Uff!

Que tal começar do começo? Vamos entender os passos de setup?

1- A primeira coisa que fizemos foi iniciar dois Eshell que, na verdade, tornaram-se dois nós do nosso cluster de runtimes Erlang.

Todas as vezes que você inicia um Eshell com a flag -sname, o que você está fazendo é criar um nó de runtime Erlang na sua rede. Na verdade, isso pode ser feito também com a flag -name.

Qual a diferença entre elas?

-sname cria o nó e dá a ele um “short name”: nome_do_no@host;
-name, pelo contrário, cria o nó dá a ele um “long name”: nome_do_no@ip_address ou nome_do_no@host.local, no caso de rede local.

Você pode perfeitamente iniciar diversos nós em uma mesma máquina, como fizemos a pouco. No entanto, você não pode duplicar os nomes dos nós, uma vez que estes são usados para identificá-los no cluster e torná-los comunicáveis uns com os outros.

“Lembrando que o nome dos nós são sempre sufixados pelo nome/ip do host, não há problema algum em ter o mesmo nome de nó em máquinas diferentes.”

Uma outra coisa interessante de aprender aqui é como os nós se comunicam. Toda comunicação entre nós é feita por um daemon chamado epmd. Este programa faz parte do sistema de runtime Erlang e tem um papel fundamental em sua arquitetura de clusters, porque sempre que “o primeiro nó” é iniciado numa máquina, automaticamente, ele também é iniciado, caso ainda não tenha sido, e passa a escutar mensagens na porta 4369 e rotea-las para o nó apropriado.

“Independente de quantos nós estejam rodando numa máquina, sempre haverá apenas uma instância do epmd rodando.”

Por fim, ainda sobre o setup dos nós do cluster, há uma outra flag que não demonstrei no exemplo acima, mas que é bem interessante e você depois pode pesquisar a respeito.

-setcookie define um “secret cookie” que serve como um tipo de token para autenticar a comunicação entre os nós do cluster. Isso quer dizer que, somente os nós iniciados com um mesmo cookie podem se comunicar. Por outro lado, sempre que um nó é iniciado sem especificar um cookie, o runtime Erlang toma a liberdade de cria um e gravar no arquivo ~/.erlang.cookie. Nestes casos, todos os nós não iniciados com um cookie compartilharão o mesmo cookie guardado nesse arquivo.

No caso do nosso exemplo ultra simples, um cookie poderia ser usado para criar uma sala de bate-papo, onde: se um nós é iniciado informando um cookie, ele entra na sala equivalente; no entanto, quando não informado, vai para uma sala genérica.

Interessante? Pois é… E esse assunto acaba puxando o da flag -hidden, que você pode aproveitar e pesquisar também.

2- A segunda coisa que fizemos no setup foi registrar um processo alvo de nossas mensagens.

Como a idéia aqui é explicar – um pouco, porque ainda há uma porção de coisas que poderiam ser faladas, mas isso deixaria esse post imenso! Dê uma olhada na documentação oficial como funciona a arquitetura de cluster de runtimes Erlang e mostrar como este provê uma maneira simples de programação distribuída, preferi não escrever um programa de chat, mas apenas usar o próprio Eshell para isso.

Com este pré-requisito em mente, o que fiz foi apenas registrar o próprio processo do Eshell – usando self(), que você entende como funciona com um nome especifico em cada nó do cluster (console_jose para um e console_maria em o outro) para fazer a troca de mensagens entre eles.

E o bate-papo, como é que rolou?

Essa é a parte mais fácil da coisa, na minha opinião. Se você já lidou com processos em Erlang, vai tirar de letra.

1- Trocamos mensagens entre os dois processos de maneira bem semelhante ao que fazemos normalmente com processos contidos num único runtime.

A única diferença aqui é que, em vez de simplesmente codarmos algo como console_jose ! “Show de bola”, codamos {console_jose, ‘jose@codezone’} ! “Show de bola”. Ou seja, além no nome do processo, informamos também em que nó do cluster ele está, tudo em uma tupla.

Um exercício legal de você fazer é o seguinte…

a. No Eshell da Maria execute:

(maria@codezone)5> console_maria ! "Show de bola".
(maria@codezone)6> flush().

b. Depois, ainda no Eshell dela, execute:

(maria@codezone)7> console_jose ! "Show de bola".

O que aconteceu? Na primeira execução, o Eshell da Maria recebeu uma mensagem, correto? Mas na segunda execução, o Eshell “crasheou”. Por quê? Porque ele não reconheceu o átomo console_jose como sendo o nome de um processo, uma vez que o processo registrado como console_jose está em outro nó do cluster. Aliás, como o Eshell da Maria crasheou, se você tentar enviar mensagens a ele novamente, como fez anteriormente e havia funcionado, ele vai crashear novamente, porque a referência ao processo que havia sido registrada tornou-se inválida.

Você precisa ficar muito atento a isso, porque quando você manda um mensagem para um processo que está em outro nó, mesmo que ele não exista, você não receberá erro – exatamente como acontece com envio de mensagens pra processos locais inexistentes.

Quer ver? Faça o seguinte teste:

(maria@codezone)8> {console_jose_123, 'jose_abc@codezone_xyz'} ! "Nao vai crashear".

E agora, o que aconteceu? Nada!

2- Outra coisa que fizemos foi usar os truques de flush() e regs(), mas esses já são bem conhecidos.

Se você costuma usar o Eshell, com certeza, já deve ter usado flush() e regs(). Caso ainda não tenha usado, aqui vai um resumo:

- flush() descarrega as mensagens contidas na caixa postal do Eshell (que como você sabe, nada mais é do que um processo Erlang qualquer);
regs() lista os processos registrados do runtime em execução.

Só o gostinho

Como disse em algum canto nesse post, ainda há bastante o que falar sobre programação distribuída com Erlang. Só pra variar, esse post foi só pra dar o gostinho. Ainda há muito mais!

Bem, quem sabe qualquer dia desses não perco o sono novamente e escrevo um pouco mais sobre o assunto. Quem sabe?

Um pouco de TDD com Eunit

No final do ano passado estava bem empolgado com a implementação Erlang do Restfulie e, algum tempo depois de ter começado o projeto, resolvi deixar de ser cowboy e escrever alguns testes. De lá pra cá, vez ou outra, dei uma melhorada nos testes, primeiro pra ficar mais útil, depois pra deixar mais fluente. Ainda não está lá essas coisas, mas já está, no mínimo, interessante. Se você quiser, pode conferir no meu GitHub.

Bem, no final das contas, isso acabou me motivando a escrever esse post, como um tutorial básico de TDD (talvez alguns queriam chamar de BDD, não tem problema, podem chamar como assim) com Eunit, a biblioteca de testes padrão que vem com qualquer instalação de Erlang.

A idéia desse tutorial é seguir numa linha um pouco diferente da documentação do Eunit e dos exemplos contidos no próprio projeto, escrevendo testes mais fluentes e interessantes de ler.

Mão na massa

Passo 1: criar a estrutura do projeto

O nome do nosso projeto será making_tdd_with_eunit (bem criativo!). Vamos criar então a seguinte estrutura de diretórios, que é frequentemente encontrada em projetos Erlang:

- making_tdd_with_eunit
  |
  +-- ebin
  +-- src
  +-- test

OK, apesar dos diretórios terem nomes auto explicativos, não custa nada reforçar, né?

ebin, contém os binários (.beam) resultantes da compilação dos fontes;
src, contém os fontes (.erl) dos módulos;
test, contém os fontes (.erl) dos testes.

Passo 2: escrever o primeiro teste que inevitavelmente falhará

Qual é o fluxo de trabalho com TDD mesmo, hein? Escrever o código a ser testado, rodar e, se funcionar, escrever o tal teste que passa de primeira, não é isso? Não, não é. Isso não é TDD.

Pois muito bem, vamos fazer então como realmente tem que ser feito. No diretório test, crie o seguinte teste (mobile_test.erl):

-module(mobile_test).

-include_lib("eunit/include/eunit.hrl").

describe_client_test_() ->
  {"Mobile",
    [fun should_have_a_number/0]}.

should_have_a_number() ->
  ?assertMatch({number, _}, mobile:number()).

Feito isso, compile e rode o teste com os seguintes comandos:

$ erlc -o ebin/ test/mobile_test.erl
$ erl -pa ebin/ -noshell -run mobile_test test -run init stop

Qual foi o resultado? O teste falhou, certo? Pois é, como manda o figurino!

“Escreva um teste, rode todos os testes e veja-o falhar; escreva o código mais simples possível para fazê-lo passar, mesmo que num primeiro momento não seja o mais bonito e eficiente; rode todos os testes e veja-os passar; depois, refatore para remover duplicações.” [1]

Passo 3: fazer o teste passar

Agora vamos fazer esse teste passar. No diretório src, crie o seguinte módulo:

-module(mobile).

-export([number/0]).

number() ->
  {number, "1212-1212"}.

Como fizemos anteriormente – agora acrescentando o módulo mobile à compilação –, compile e rode o teste com os seguintes comandos:

$ erlc -o ebin/ src/mobile.erl test/mobile_test.erl
$ erl -pa ebin/ -noshell -run mobile_test test -run init stop

Resultado? “Test passed.”

Passo 4: escrever mais testes

Só para não prolongar muito – este que deveria ser um pequeno tutorial – vamos para o ponto em que magicamente passamos a ter testes para as quatro funções (ultra dummies) contidas no módulo mobile, tudo bem?

-module(mobile_test).

-include_lib("eunit/include/eunit.hrl").

describe_client_test_() ->
  {"Mobile",
    [fun should_have_a_number/0,
     fun should_have_a_area_code/0,
     fun should_have_a_company/0,
     fun should_have_a_owner/0]}.

should_have_a_fixed_number() ->
  ?assertMatch({number, "1212-1212"}, mobile:number()).

should_have_a_fixed_area_code() ->
  ?assertMatch({area_code, "11"}, mobile:area_code()).

should_have_a_fixed_company() ->
  ?assertMatch({company, "DEAD"}, mobile:company()).

should_have_a_fixed_owner() ->
  ?assertMatch({owner, "Little Jose"}, mobile:owner()).
-module(mobile).

-export([number/0, area_code/0, company/0, owner/0]).

number() ->
  {number, "1212-1212"}.

area_code() ->
  {area_code, "11"}.

company() ->
  {company, "DEAD"}.

owner() ->
  {owner, "Little Jose"}.

OK, repetindo o passo anterior de compilação e execução dos testes, temos quatro testes passando, certo? Então é hora de um pouco de refatoração…

Passo 5: refatorar os testes para ficarem mais fluentes

Nesse ponto, vamos adicionar mais fluência ao nosso teste e melhorar a sua comunicação, inclusive, descrevendo um cenário “bem interessante”.

-module(mobile_test).

-include_lib("eunit/include/eunit.hrl").

describe_client_test_() ->
  {"Mobile",
    {"when is a dummy",
      [
        {"should have a fixed number",
          fun should_have_a_fixed_number/0},
        {"should have a fixed area code",
          fun should_have_a_fixed_area_code/0},
        {"should have a fixed company",
          fun should_have_a_fixed_company/0},
        {"should have a fixed owner",
          fun should_have_a_fixed_owner/0}
      ]}}.

should_have_a_fixed_number() ->
  ?assertMatch({number, "1212-1212"}, mobile:number()).

should_have_a_fixed_area_code() ->
  ?assertMatch({area_code, "11"}, mobile:area_code()).

should_have_a_fixed_company() ->
  ?assertMatch({company, "DEAD"}, mobile:company()).

should_have_a_fixed_owner() ->
  ?assertMatch({owner, "Little Jose"}, mobile:owner()).

Compilando e rodando os testes: “All 4 tests passed.”

Próximo passo: escrever mais testes e refatorar o módulo mobile

Até aqui, temos um módulo mobile totalmente dummy e um teste que usa apenas a macro ?assertMatch. Ainda há muito que pode ser feito, desde adicionar algum comportamento útil ao módulo mobile, até melhorar os testes, fazer diferentes asserções, adicionar befor_all e after_all (como nos testes do Restfulierl, por exemplo) para estado inicial e final, e por aí vai.

Bem, esse passo deixo por sua conta. É um bom exercício pra você praticar mais Erlang e Eunit.

Espero que tenha gostado e até mais!

[1] Beck, K. Test-Driven Development: By Example. Addison-Wesley Professional, 2002.

Geração e build de projetos Erlang/OTP

OK. Hora do jabá…

Há alguns meses, quando comecei a mexer mais com Erlang/OTP, senti a necessidade de ter uma ferramenta que gerasse a estrutura dos projetos, bem como stubs dos módulos necessários. Não encontrei nenhuma que fosse realmente simples. O que fiz? Cocei minha própria coceira, crie o projeto otp_kickoff.

Agora, de saco cheio desconfortável de usar Make para automatizar tarefas de build, resolvi coçar minha própria coceira novamente, crie o projeto ebuilder.

Se você também está mexendo com Erlang/OTP, sugiro que dê uma olhada nestes projetos, quem sabe eles também não facilitam sua vida, ahmmm?

Erlang/OTP no Caelum Tech Day 2009

Hoje aconteceu o Caelum Tech Day 2009, um evento de tecnologia voltado à própria galera que trabalha na Caelum, que ano passado contou com nada menos que Jim Weber falando de RESTful, com exclusividade, antes de sua fantástica palestra no Falando em Java.

O objetivo do evento é compartilharmos entre nós mesmos as coisas que estudamos durante todo o ano. Levantamos então uma porção de assuntos que gostariamos de compartilhar, votamos nos que gostaríamos de ver, e no final, os mais votados são apresentados.

Este ano não tivemos o Jim, mas tivemos muita gente boa da Caelum falando sobre Ruby, JavaScript, Scala, Java, o Guilherme Silveira falando de RESTful junto com o Cauê Guerra e eu falando de Erlang/OTP junto com o Thadeu Russo.

Veja a nossa apresentação logo abaixo e divirtam-se com Erlang!

Entendendo um tiquinho de self()

Uma confusão bem comum quando se começa a escrever programas concorrentes em Erlang é quanto ao uso da BIF (built-in function) self(). Mais especificamente, quanto ao seu retorno.

A BIF self() é analoga ao this do Java, por exemplo, que é capaz de responder quem é o objeto contenedor do método atualmente em execução. Semelhantemente, em Erlang, self() é capaz de dizer quem é o processo contenedor da função atualmente em execução avaliação. Assim, se você chamar self() no Erlang shell, você vai receber como retorno o Pid (identificador de processo) do próprio Erlang shell.

Faça um teste no seu Erlang shell:

1> self().

Você deve ter recebido algo semelhante a <0.31.0> com retorno. Isto porque o Erlang shell nada mais é do que um processo Erlang com um comportamento REPL.

Ok. Agora, o que acontece se você tiver um programa com um único módulo em que há duas funções que trocam mensagens entre dois processos? Qual seria o retorno de self() nestas duas funções?

Um pequeno exemplo

Vejamos um exemplo bem simples deste caso extraído do livro Erlang Programming:

-module(add_two).
-export([start/0, request/1, loop/0]).

start() ->
  process_flag(trap_exit, true),
  Pid = spawn_link(add_two, loop, []),
  register(add_two, Pid),
  {ok, Pid}.

request(Int) ->
  add_two ! {request, self(), Int},
  receive
    {result, Result}       -> Result;
    {'EXIT', _Pid, Reason} -> {error, Reason}
    after 1000             -> timeout
  end.

loop() ->
  receive
    {request, Pid, Msg} ->
       Pid ! {result, Msg + 2}
  end,
  loop().

De maneira bem objetiva, o que este código faz é o seguinte:

1- Quando um processo já existente – que no nosso caso será o próprio Erlang shell – faz uma chamada à função start(), um novo processo é gerado, tendo como ponto de partida a função loop(), o seu identificador é associado à variável Pid e, por fim, ele recebe o apelido add_two.

2- Todas as vezes que a função request(Int) é chamada, uma mensagem é enviada para o processo add_two, para que este some 2 ao número passado como parâmetro e envie o resultado de volta ao processo solicitante.

3- Sempre que o processo add_two recebe uma nova mensagem, esta é capturada na sentença receive ... end da função loop(), que verifica se é um “pedido de soma”, e então, envia o resultado da soma ao processo solicitante, identificado por Pid.

A confusão

Bem simples mesmo, certo? Então, por que acontece a tal confusão?

A confusão acontece, porque o retorno de self() não é o mesmo em todas as funções deste módulo. Isto porque a função loop(), apesar de estar contida no mesmo módulo que as funções start() e request(Int), não está rodando sendo avaliada no mesmo processo que elas estão. A função loop() está sendo avaliada no processo add_two, enquanto que start() e request() estão sendo avaliadas no primeiro processo – o Erlang shell. Assim, self() em loop() retorna um identificador de processo diferente do que retornaria as demais funções.

Quer tirar a prova?

Mais um simples exemplo, só que desta vez, esclarecedor!

Eu adicionei um bocado de “prints” no código que apresentei anteriormente e se você executá-lo agora, terá a prova do que foi discutido. (Tá tudo bem, você não precisa de uma prova a estas alturas do campeonato, mas vai ser divertido.)

-module(add_two).
-export([start/0, request/1, loop/0]).

start() ->
io:format(": start -> self() = ~w~n", [self()]),
  process_flag(trap_exit, true),
  Pid = spawn_link(add_two, loop, []),
  io:format(": start -> Pid  = ~w~n", [Pid]),
  register(add_two, Pid),
  {ok, Pid}.

request(Int) ->
  io:format(": request -> self() = ~w~n", [self()]),
  add_two ! {request, self(), Int},
  receive
    {result, Result}       -> Result;
    {'EXIT', _Pid, Reason} -> {error, Reason}
    after 1000             -> timeout
  end.

loop() ->
  receive
    {request, Pid, Msg} ->
       io:format(": loop/receive -> self() = ~w~n", [self()]),
       io:format(": loop/receive -> Pid    = ~w~n", [Pid]),
       Pid ! {result, Msg + 2}
  end,
  loop().

Após fazer a devida compilação, faz o seguinte teste no Erlang shell:

1- Veja o identificador de processo (aka Pid) do Erlang shell:

1> self().

2- Inicie o processo add_two:

2> add_two:start().

3- Chame a função request(Int):

3> add_two:request(10).

Executou? Comparou os Pids? Viu a diferença de escopo entre as três funções? Pois muito bem, então fica aqui a lição:

“Módulos servem para agrupar funções com um mesmo escopo conceitual, mas nem sempre com o mesmo escopo de processo”

Você deveria considerar Erlang para seu próximo grande projeto

Ao ler o título deste post, talvez você esteja se perguntando: por que eu deveria considerar Erlang para meu próximo grande projeto? Bem, meu objetivo com este post é te apresentar alguns importantes motivos para fazer isto.

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, o grande Joe Armstrong 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 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 tem sido evoluida e usada para escrever grandes sistemas críticos, porque é exatamente nesse cenário que Erlang faz mais sentido. Se seu projeto é construir um sistema crítico, altamente tolerante a falhas, com disponibilidade 24×7 e veloz como o papa-léguas, sim, Erlang é para você. Mas se não é bem esta sua necessidade, se você quer apenas construir um pequeno sistema, com baixa concorrência, poucos usuários, pouca necessidade de performance e possibilidade de passar horas down em manutenção, não, você não precisa de Erlang. Que tal Basic?

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.

Quem usa Erlang atualmente?

Além da Ericsson, é lógico, há algumas outras grandes empresas e projetos usando Erlang, como por exemplo:

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 seu poderoso EC2;
Motorola, CouchDB, RabbitMQ, Ejabbed, etc.

Ok, mas Erlang é propriedade da Ericsson?

Não, felizmente, não. Em 1998 a Ericsson tornou Erlang open source sob a licença EPL.

Quer mais uma boa notícia? Você não precisa de um servidor de aplicações para rodar sua aplicação Erlang, nem mesmo uma pesada IDE para escrevê-las. Tudo o que você precisa é de uma distribuição de Erlang para sua plataforma, seja Mac OS X, Linux, Windows, Solaris, ou qualquer outro sistema Unix-like, que traz consigo máquina virtual, compilador e vasta bibliotéca de módulos muito úteis – além do banco de dados Mnesia; e um editor de textos de sua preferência TextMate, por exemplo, tem um ótimo bundle para Erlang.

Algumas características de Erlang

1- Expressividade e beleza

Há quem diga que não, mas Erlang é uma linguagem muito bonita — ao menos pra mim. 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 torna seu código fácil de ler, de escrever e de 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

Isto certamente é uma 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 – e sem estourar o “heap” de memória.

3- Livre de efeito colateral

Uma das grandes capacidades dadas por Erlang é a construção de sistemas altamente concorrentes rodando em processadores com multiplos núcleos. Isto não combina nada com efeito colateral, por isso, em Erlang, uma vez que um dado valor tenha sido atribuído a uma variável, esta não poderá mais ser modificada – ou seja, estão mais para o que conhecemos por constantes do que para o que conhecemos por variaveis.

Se você já escreveu código concorrênte 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 concorrênte repleto de efeitos colaterais.

4- Pattern matching

Pattern matching em Erlang é usado para associar valores a variáveis, controlar fluxo de execução de programs, extrair valores de estruturas de dados compostas e lidar com argumentos de funções. Mais um ponto para código declarativo e expressividade. Vejamos o código abaixo:

-module(sample2).
-export([convert_length/1]).

convert_length(Length) ->
    case Length of
        {centimeter, X} ->
            {inch, X / 2.54};
        {inch, Y} ->
            {centimeter, Y * 2.54}
    end.

Fala por si, não?

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 sim, algumas coisas são particularmente interessantes aqui:

a. 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 [caríssimas];
b. 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++.

Gostei de Erlang, há algum livro para eu começar a estudar?

Sim, há dois livros excepcionais. Um, do próprio criador da linguagem, Joe Armstrong:

E outro de Francesco Cesarini e Simon Thompson:

Além disso, há o próprio material on line que é muito bom e barato. :)

Conclusão

Erlang possui muitas outras características e informações bem interessantes, mas que me falta espaço neste post para citar e apresentar, senão ele ficará absurdamente gigantesco para o meu gosto. Mas acredito que pelo que já apresentei até aqui, você já tenha motivos suficientes para pensar em Erlang com carinho e conciderá-la para seu próximo grande projeto.

Em breve devo escrever algo sobre OTP, já que neste post apresentei características mais inerentes à linguagem em si e nem tanto sobre a plataforma.

Até mais!

Quer saber onde será seu próximo trabalho?

Então não deixe de visitar…

O site é recém-nascido, mas já tem uns recursos bem legais, como sistema de pesquisa por palavras-chave, núvem de tags e entre outros. Vale a pena conferir e acompanhar sua evolução – e oportunidades de bons trabalhos.

Mais uma iniciativa bem interessante da Caelum. :)