Prólogo

Se você, como eu, conheceu Ruby através do rails, certamente iniciou seu aprendizado com o livro agile web development with rails. Depois de brincar um pouco com Rails, você por ora começou a ficar mais interessado em Ruby. Ruby com certeza é uma ótima linguagem, mas leva um tempo relativamente maior que o usual para programar efetivamente.

 

Programar efetivamente em Ruby significa conhecer muito bem a programação de objetos e meta-objetos, ou melhor, meta-programação. Esse novo tipo de programação tem seus pontos fortes e pontos fracos, assim como qualquer outra coisa em software.

 

Tenha em mente que meta-objetos é um tipo de meta-programação. No entanto, meta-programação não é somente meta-objetos. Para deixar este texto mais limpo, vou explicar detalhadamente ao decorrer do artigo.

 

Enfim, muitos dizem que a meta-programação é a mágica do Ruby, eu até concordo, bem… parcialmente…

 

Receitas de um desastre

Indubitavelmente, um projeto com excessiva meta-programação é um projeto confuso, complexo e irritante. Aquelas mensagens de ArgumentError, NameError, que uma vez foram tão claras, já não começam a ser mais explicativas. A razão disso? é simples, estas mensagens são geradas em pontos muito profundos do código.

 

Meta-programação envolve uma série de chamadas em tempo de execução/avaliação, geralmente envolvendo uma ou diversas, tendo como inputs, o resultado de outras chamadas, avaliações de sub-códigos, classes programáveis e claro, inputs dos próprios desenvolvedores. Muitas vezes, esses inputs somados a natureza imprevisível de um código em tempo de execução, e certos comportamentos, resultam numa perfeita receita para o desastre.

 

Quando a meta-programação começa a ter uma extensão mais profunda, o pessoal logo nome-a como DSLs (Domain Specific Language(s)). Neste caso, algo mais interessante acontece comparado a programação usual. Neste momento, a meta-programação passa a ter conhecimento e controle sobre a linguagem e o domínio ao qual está sendo aplicado.

 

Se sua linguagem de programação suporta mecanismos de circularidade, isto é, reflexão, macros, templates, meta-objetos ou qualquer outro tipo de coisa que possa ser avaliável ou gere funcionalidade que a linguagem compreenda, então, muito provavelmente você poderá criar DSLs. Repare que a meta-programação envolve o uso de um ou vários desses recursos citados acima.

 

Em teoria isso é perfeito, no entanto, o mundo em prática nos reserva grandes surpresas. Como descrito anteriormente, erros gerados em meta-programação são erros gerados nas profundezas dos códigos. São erros que não fazem sentido, são erros complicados de debugar e navegar.

 

Não apenas isso, a medida em que se usa descontroladamente recursos de meta-objetos, reflexão, macros, o desenvolvedor passa a perder interatividade com a linguagem. A linguagem passa a perder interatividade consigo mesma. Como resultado, a meta-programação começa a inferir novos desafios ao desenvolvedor.

 

Acredite se quiser, a coisa mais desafiante quando meta-programando é lidar com um sistema razoável de avaliação. Isso sempre faz-me pensar cinco vezes antes de usar qualquer recurso de meta-programação.

 

 

Lado negro da feitiçaria

Embora argumentáveis, gostaria que o leitor assumisse essas duas afirmações como verdades:

 

  1. DSL é uma nova linguagem construida no topo de uma outra linguagem;
  2. DSL toma o mecanismo de avaliação da linguagem base como seu;

 

Desenvolvedores, em sua maioria, não dão muita importância a essas coisas. Eles colocam confiança demais na linguagem de base e esquecem o domínio. Lembrem-se, DSLs são tudo sobre domínio, por sua vez, domínio não é quase nada sobre linguagem.

 

Embora não seja elegante ou ético apontar projetos de companheiros Rubystas como casos malsucedidos, as vezes se faz necessário. É como dizem por aí, “entrou na água, é para se molhar”.

 

O projeto que eu ponho em discussão é rSpec. Para quem não conhece, rSpec, é uma biblioteca de testes, assim como o tão popular test/unit do ruby.

 

Minha recomendação, como desenvolvedor, que já usou e usa meta-programação em diversos projetos. Projetos criados por mim e por outras pessoas, é fazer uso desses recursos somente quando realmente necessário.

 

Desde que comecei a mexer com software, tenho um amigo que sempre martelava em minha cabeça sobre manter as coisas simples. Diga-se de passagem, demorei muito para aprender isso.

 

Desde então, sigo uma linha muito direta e extremista de raciocínio. Se as coisas podem ser feitas simplesmente, não vale o esforço tentar sofistica-las. Simples assim.

 

Tenha em mente esse pedaço de código em rSpec:

 

describe Bowling do

  before(:each) do

    @bowling = Bowling.new

  end

  it 'should score 0 for gutter game' do

    20.times { @bowling.hit(0) }

    @bowling.score.should == 0

  end

end

Agora com a biblioteca padrão de testes:

class BowlingTestCase < Test::Unit::TestCase

  def setup

    @bowling = Bowling.new

  end

  def test_score_0_for_gutter_game

    20.times { @bowling.hit(0) }

    assert_equal @bowling.score, 0

  end

end

 

 

Será que justifica codificar 10.000 linhas de meta-programa para cumprir apenas uma função estética? Isto é, trocar class BowlingTestCase por describe Bowling? Será que DSLs valem mais de 3.000 commits num repositório? será?

 

É claro que rSpec não compreende apenas isso. É possível usar um monte de outras funcionalidades. De qualquer forma, rSpec tornou testes, uma tarefa trivial, que é realizado simplesmente, em algo muito sofisticado.

 

Conclusão

Se a meta-programação não for muito bem planejada e desenvolvida, problemas com avaliações, erros, comportamentos inesperados, tolenadas de código mágicos serão alguns dos desafios. Para contornar esses desafios, o desenvolvedor tem de programar sistematicamente e acima de tudo, entender profundamente cada ponto do sistema e do domínio de forma que possa criar um sistema de avaliação apropriado.

 

Se minhas palavras têm sido duvidosas, sugiro que o leitor dê uma olhada no repositório do rSpec no rubyforge.

 

Antes de mergulhar, devemos pensar se realmente vale a pena tornar tarefas tão triviais (como testar pedaços de código) em algo tão elaborado. Devemos pensar se isso realmente vai nos trazer algum avanço ou produtividade como resultado final.