Existe um conceito, muito utilizado por programadores de linguagens funcionais e pouco utilizado em outras linguagens, chamado Lazy Evaluation. Para não extender muito a definição, basta dizer que no caso do Lazy Evaluation, um resultado só é computado quando ele é necessário ao programa. Exemplos explicam melhor o conceito, então segue um:

100.times do |iterador| #Equivalente ao "for" de outras linguagens
  n = fatorial(iterador)  #Calcula o fatorial
  if(iterador.even?)       #Se for um número PAR
    print "Resultado da operação: #{n}" #Imprime o resultado do fatorial
  end
end

Lembrando que Ruby não trabalha com Lazy Evaluation, portanto o código acima não seria adequado. Mas, digamos que a linguagem acima suporte Lazy Evaluation: o resultado de n só seria calculado se o iterador for par. Ou seja, embora estejamos sempre definindo que n = fatorial(iterador), o programa não calcula o resultado do fatorial até que precisemos dele – neste caso, até que ele seja impresso na tela. Ou seja, em linguagens como Haskell, que suportam Lazy Evaluation por padrão, o código acima seria perfeitamente válido e não seria ineficiente. Normalmente, quando você usa Lazy Evaluation o código que só rodará depois é chamado de “promisse”, pois ele é uma promessa que o valor será calculado.

Mas porque isso é tão interessante?

Justamente, porque atrasar o cálculo de algo pode te dar maior fluidez em alguns outros momentos. Já citei anteriormente sobre o DataMapper, que na hora de fazer uma busca retorna apenas uma “promessa” que que irá buscar, e se você precisar apenas do primeiro elemento do Array ele buscará apenas o primeiro elemento. Também, no MongoParadigm (meu projeto de um mapeador para MongoDB) estou tentando deixá-lo Lazy, pois gostaria mesmo de aproveitar alguns conceitos do DataMapper para o Mongo. Mas esses dias, tive um problema diferente:

Estava trabalhando num passatempo, uma interface para listar os documentos do MongoDB. Estava usando Qt4, Ruby, e eu tinha uma coleção de documentos com 600 mil documentos (imagine 600 mil registros numa tabela, como comparação). O problema é que eu exibiria apenas um elemento por vez, e teriam dois botões (próximo e anterior), e não faria sentido nenhum ficar buscando os documentos sempre que eu apertasse em próximo ou anterior (afinal, é um projeto offline e eu queria fluidez total). Também, seria impossível buscar os 600 mil de uma vez e popular um Array com todos eles, afinal isso demoraria muito tempo e acabaria com a idéia de fluidez. Então, pensei em usar Threads, mas… Qt4 não suporta Threads do Ruby.

O que fazer? Aprender GTK para Ruby, e ter que lidar com uma mudança de quase todo o código? Mudar de linguagem? Achar um jeito de rodar Threads nativas em Ruby, e sabe-se lá como lidar com essa nova complexidade? usar um timer e ficar buscando de tempos em tempos novos objetos?

A solução foi razoavelmente simples: criar um “buscador”, que mantém um registro de qual busca foi feita e do número de registros que a coleção possui. Criar um “Hash” para armazenar todos os registros, e a partir daí, sempre que um registro for requisitado, e se ele não existir no Hash, ele é buscado (junto com mais alguns registros à frente dele) e armazenado no hash. Desta forma, fica bem mais fluído e até mesmo é uma solução bem mais elegante do que o uso de Threads.

Mas… até quando usar Lazy Evaluation?

Note que não é uma “bala de prata”, que nem todos os problemas são solucionado usando Lazy Evaluation. Algumas vezes, usar Lazy Evaluation mais atrapalha do que ajuda – especialmente nas linguagens como Ruby que não a suportam nativamente. Isso porque, algumas vezes, você soluciona um problema mas deixa seu programa mais difícil de entender, ou os erros não ficam muito visíveis.

Mais à frente, eu tento postar novidades sobre o assunto. Por hora, a interface gráfica está no meu GitHub, basta cloná-la e testá-la (note que você precisará do driver Mongo para Ruby e do Qt4).