[Tradução] Três Contextos Implícitos em Ruby

2009 November 16, 15:28 h - tags: beginner translation learning ruby

[Tradução] Três Contextos Implícitos em Ruby

A desenvolvedora japonês @yugui escreveu um grande complemento ao artigo anterior sobre metaprogramação do Yehuda, que traduzo logo abaixo:

Yehuda Katz escreveu um artigo sobre self e metaclass. Nesse artigo ele disse que Person.instance_eval associa a metaclass de Person para self para uma classe Person, mas isso é obviamente errado.

1
2
class Person; end  
Person.instance_eval{ p self }  #=> Person

Como mencionei em um artigo antigo, embora eu deva me desculpar por estar escrito em japonês, Ruby sempre tem 3 contextos implícitos: self, o chamado ‘klass’ e o ponto constante de definição. Yehuda está confundindo self com ‘klass’.

self

self é o self que você conhece. É o receptor padrão de invocação de método. Sempre existe um self.

1
2
3
4
5
6
7
8
9
10
11
12
p self                         # mostra "main"  
  
class Foo  
  def bar(a = (p self)) end  
end  
foo = Foo.new  
foo.bar                        # mostra "#<Foo:0x471004>"  
  
class Foo  
  class Baz < (p self; self)   # mostra "Foo"  
  end  
end

No nível superior, uma instância especial de Object chamada “main” é o self. Onde quer que esteja, você pode recuperar o self a partir da pseudovariável self.

Se você invocar um método sem explicitar um receptor, self receberá essa invocação.

o chamado ‘klass’

Eu chamei o conceito de ‘klass’ no artigo antigo, mas não sei se é o melhor nome. É a classe padrão sobre o qual o método é definido. Agora gostaria de chamá-lo “definidor padrão”.

Ruby sempre segura a referência a uma classe assim como ao self. Mas não há maneira de recuperá-lo diretamente. É mais implícito que self. Se você definir um método sem dar um receptor específico, em outras palavras, se definir um método com o jeito sintático normal de definir métodos, o definidor padrão terá o método como um método de instância.

Exemplos:

No nível superior, Object é a classe. Então funções globais são igualmente métodos de instância na classe Object como você já sabe.

1
2
def hoge; end  
Kernel.instance_method(:hoge)  #=> #<UnboundMethod: Object#hoge>

Aliás, “hoge”, “fuga”, “piyo” é japonês para “foo”, “bar”, “baz”.

A sintaxe class muda ambos os self e o definidor padrão para a classe que está agora sendo definida.

1
2
3
4
class T  
  def hoge; end  
end  
T.instance_method(:hoge)      #=> #<UnboundMethod: T#hoge>

Em um corpo normal de método, self é o receptor de invocação de métodos e o definidor padrão é a classe sintaticamente fora dela, agora ela é T.

1
2
3
4
5
6
7
8
9
class T  
  def hoge  
    def fuga; end  
  end  
end  
t = T.new  
t.hoge  
t.method(:fuga)               #=> #<Method: T#fuga>  
T.instance_method(:fuga)      #=> #<UnboundMethod: T#fuga>

Não confunda isso com def self.fuga, uma definição de método singleton. Quando você dá uma definição de método a um receptor, o método será adicionado à eigenclass do receiver.

U não tem um método de instância chamado fuga porque fuga é um método singleton de u.

Onde quer que esteja, existe um definidor padrão. Quando executa um valor padrão, o definidor padrão é a classe externa assim como no corpo do método.

Em outras palavras, a definição class muda o definidor padrão mas não a definição de método.

família eval

O que o instance_eval faz é:

  • mudar o self para o receptor do instance_eval
  • mudar o definidor padrão para o eigenclass do receptor
  • se o receptor não tiver um eigenclass ainda, cria um.
  • executa o bloco dado
1
2
3
4
5
6
7
o = Object.new  
o.instance_eval do  
  p self                       #=> #<Object:0x454f24>  
  def hoge; end  
end  
o.method(:hoge)                #=> #<Method: #<Object:0x454f24>.hoge>  
Object.instance_method(:hoge)  # raises a NameError "undefined method `hoge' for class `Object'"

Vamos lá:

Como o instance_eval muda o definidor padrão do eigenclass para $o, então fuga e piyo serão métodos singleton de $o

Oops, esqueci de mencionar que:

1
RUBY_VERSION               #=> "1.9.1".  

Ruby 1.8 age de maneira mais léxica, então você acabará tendo o contrário:

1
2
3
4
$o.method(:fuga)           # raises a NameError  
$o.method(:piyo)           # raises a NameError  
T.instance_method(:fuga)   #=> #<UnboundMethod: T#fuga>  
T.instance_method(:piyo)   #=> #<UnboundMethod: T#piyo>

Em Ruby 1.8, o definidor padrão no corpo do método é baseado lexicamente na definição da classe externa. De qualquer forma, tanto no Ruby 1.8 quanto 1.9, instance_eval muda self para o receptor, o definidor padrão a seu eigenclass.

Finalmente, class_eval muda ambos self e o definidor padrão para o receptor:

self definidor padrão
class_eval o receptor o receptor
instance_eval o receptor eigenclass do receptor


No meu artigo antigo eu discuti sobre Kernel#eval e instance_eval/class_eval com execução de Strings.

definição de constantes

Quando você vê uma variável de instância, ela é uma variável da instância de self. Quando você usa uma variável de classe, ela é uma variável de classe da classe de self; ou o próprio self quando self é uma classe.

Mas constantes se comportam de maneira diferente. É outro contexto implícito de Ruby. O Ruby Core Team chama esse conceito de “cref”.

Discutiremos esse conceito de “cref” em outro artigo.

Comments

comentários deste blog disponibilizados por Disqus