#4 Metaprogramação: Blocos

Posted on February 12, 2010

1


Obs: Esse post assume que você conhece o básico sobre blocos em Ruby.

Um código precisa de um ambiente pra ser executado, que pode ser
uma variável local, variável de instância, self…

O nome dado para essas entidades normalmente é binding. A característica principal dos blocos é que eles contém tanto o código a ser executado quanto os bindings.

Vejamos esse exemplo. Um método imprime_cor passando um bloco. Antes disso, uma variável x definida como “Azul”. A variável se encontra no mesmo escopo da chamada do método:

Qual será a resposta ? Quando você passa um bloco para um método, eles tem um conjunto de bindings diferentes entre si.
No escopo do método, a variável x vale “Vermelho”. No escopo do bloco a variável x vale “Azul”. Não existe algo como sobrescrita aqui! Os escopos são diferentes, o que faz com que o código no método não seja visível ao bloco.
Resposta: Azul

Essa propriedade é chamada de closure, o importante aqui é que o bloco captura seu conjunto de bindings e leva consigo por onde for.

Vamos entender um pouco mais sobre os Escopos (Scopes).

Scopes

De acordo com o escopo atual, voce terá dierentes bindings. O objeto atual que ve os escopos é também conhecido como self. Vamos analisar os bindings em diferentes escopos com a ajuda do método Kernel#local_variables( ).

O primeiro escopo, ainda fora da classe é chamado de top-level scope e define a variável v1. Então ele entra no escopo da classe, e o que ocorre ?

Sempre que você entra em um novo escopo os bindings anteriores são substituídos por um novo conjunto de bindings, o que significa que quando o programa entra na classe, v1 saiu do escopo e não é mais visível.

E quanto ao escopo do método? Enquanto o método não for chamado, seu escopo não existe.

Essa análise é mais fácil de ser feita conhecendo as barreiras dos escopos (Scope Gates).

Scope Gates

Existem 3 lugares que Ruby deixa o escopo anterior e cria um novo:

  • Classes (class)
  • Módulos (module)
  • Métodos (def)

A diferença sutil está entre classes/modulos e métodos. Códigos em classes e módulos são executados imediatamente enquanto códigos em métodos são executados eventualmente de acordo com a chamada a eles.

Flattening the Scope

Existe um maneira de “burlar” e passar pelas barreiras dos escopos, vejamos o código abaixo:

O problema é que a cada vez que você entra em um novo escopo, as variáveis antigas somem do escopo anterior…então como passar a variável ?

A primeira barreira é a class. Quando a classe aparece um novo escopo é criado. Porém.. se a chamada a classe fosse através de um metodo ela estaria no mesmo escopo. Você pode capturar a variável e passar esse closure ao método. Como? Criar uma classe é o mesmo que chamar seu método new!

A mesma estratégia pode ser utilizada com o método, também passando o clousure:

Essa estratégia é também conhecida como nested lexical scopes, flattening the scope, ou apenas Flat Scope.

Continua
Próximo post – Procs, lambdas e &