Metaprogramação: Eigenclass em Ruby

O modelo de objetos do Ruby não é trivial. Em códigos mais complexos não é simples saber de onde realmente vem um método ou mesmo enxergar que toda classe em Ruby é um objeto, e isso pode causar algumas confusões. Uma das partes mais interessantes do modelo de objetos Ruby é conhecida como Singleton Class. Para chegar nela, vamos analisar o código abaixo:

class Caelum
  def teste
    puts "teste"
  end
end

obj = Caelum.new
obj.teste

Nada demais até aqui. Apenas criamos uma classe e invocamos um método. Porém em Ruby nossas classes são abertas, e podemos adicionar novos métodos não só em classes já definidas, como também em objetos. Uma forma de fazer isso é:

def obj.ensina
  puts "ruby"
end

obj.ensina # => 'ruby'

Basicamente um objeto procura os métodos na sua classe (obj procura em Caelum) e, caso não encontre, sobe na hierarquia das superclasses, até Object (ou BasicObject no Ruby 1.9).

Mas onde está o método ensina na hierarquia? Ele não pode estar definido em obj, afinal obj não é uma classe, e também não pode estar na classe Caelum, ou todas as instancias da classe Caelum também teriam esse método.

Eigenclasses

O fluxo comentado acima não está totalmente completo. Cada objeto em Ruby pode ter sua própria classe, uma classe especial, escondida. Existe uma sintaxe especial para acessar essa classe, que é:

class << obj
end

Qualquer método definido dentro do código acima pertenceria apenas aquela instância. A classe acima é chamada de Eigenclass (ou Singleton Class). O modelo de objetos Ruby agora fica da seguinte forma:

Eigenclasses e Method Lookup

Seguindo o modelo de objetos Ruby, um objeto procura um método de instância na sua Classe. Caso não ache, sobe para a superclasse da classe.

class Caelum 
    def ensina

    end 
end

class Sala < Caelum 
  
end 

obj = Sala.new
obj.ensina 

No exemplo acima, obj procura o método ensina na classe Sala. Como não encontra, sobe até a superclasse que é Caelum. Onde entra a Eigenclass nisso ?

Primeiro vamos criar um método em todos os objetos que retorne a Eigenclass de um objeto:

class Object 
  def eigenclass 
    class << self; self; end 
  end 
end 

p "caelum".class		#=> String
p "caelum".eigenclass	#=> #Class:#String:0x18ba78

Repare que o Ruby coloca um sinal # na frente da Eigenclass. Usaremos esse sinal apartir de agora para identificar esse tipo de classe, sendo #obj a Eigenclass do objeto obj.

Agora vamos criar um método em obj colocando-o na sua Eigenclass e verificar a hierarquia:

class << obj 
  def eigen_method
    'obj#eigen_method()' 
  end 
end 

p obj.class				#=> Sala
p obj.eigenclass			#=> #obj
p obj.eigenclass.superclass	#=> Sala

A superclasse de uma Eigenclass é a própria classe do objeto. Ou seja, se um objeto possui uma Eigenclass, Ruby inicia a procura do método nela. Caso não encontre, sobe para a primeira superclasse (que é a própria classe do objeto).

Eigenclasses e herança

Para completar o modelo, quem é a superclasse de uma Eigenclass? Vamos adicionar uma eigenclass na classe Caelum e analisar o modelo:

class Caelum 
    class << self 
      def meu_eigen_method 
        'Caelum.meu_eigen_method()' 
      end 
    end 
end      

p Caelum.eigenclass				# => #Caelum
p Sala.eigenclass				# => #Sala
p Sala.eigenclass.superclass		# => #Caelum
p Caelum.eigenclass.superclass	# => #Object     

Repare que a Superclass da Eigenclass de Sala (Sala.eigenclass.superclass) é a eigenclass da classe Caelum! Ou seja, A superclasse de uma eigenclasse é a eigenclass da superclasse do objeto! Não muito trivial.

Matz, o criador do Ruby não chegou a definir um nome para esse tipo de classe, sendo que os programadores costumaram chamá-la de singleton class por conta própria. Como muita gente acabou confundindo com o design pattern de mesmo nome, outras opções começaram a surgir. Eingenclass surgiu na Alemanha onde eigen tem o significado de “próprio de algo”, sendo uma tradução comum algo como “a própria classe do objeto”.

10 Comentários

  1. Roger Leite 10/03/2010 at 08:32 #

    Muito legal o post!
    Realmente a “eigenclass” foi novidade pra mim, só conhecia como “metaclass”.

    Sucesso!

  2. Dann 10/03/2010 at 12:55 #

    Nossa, ruby viaja d+!
    Muito legal!
    Parabens

  3. Rafael 10/03/2010 at 14:42 #

    Tá aí uma explicação que eu queria ver há tempos.
    Legal mesmo, parabéns Anderson!

  4. Anderson Dias 11/03/2010 at 08:49 #

    Muito bom esse post! Parabéns chará!

  5. Hugo Rosa 11/03/2010 at 11:35 #

    Muito boa a explicação já tinha visto, mas não traduzida.
    Também gosto no termo eigenclass.
    E para quem gostariam de traduzir, eu recomendo “autoclasse”, caso o termo não esteja sendo usado com outro significado.
    A origem eu tirei da matemática, onde os termos eigenvalue e eigenvector (também de origem alemã) são traduzidos como autovalor e autovetor. Já que o prefixo auto- possui o mesmo sentido do prefixo eigen-.

  6. Sergio Oliveira Jr. 13/03/2010 at 06:35 #

    Isso é meio complicado mesmo. Eu fiz um blog sobre isso também, como referência para quando eu me esquecesse desse esquema não muito trivial.

    A eigenclass é trivial, o que não é trivial é o conceito de metaclass. O problema é que em Ruby, muito diferentemente de Java, uma class é um objeto instanciado! Isso é confuso. Então quando vc adiciona um novo método a class, para afetar todas as instâncias mesmo, o Ruby cria uma Eingenclass para a INSTANCIA DA CLASSE, que chamamos de METACLASS. A metaclass é uma eingenclass também, mas não de qualquer instancia mas sim de uma instancia de classe.

    A confusão é tentar entender a diferença de EIGENCLASS e METACLASS.

    Voces concordam com tudo isso? Meu post onde eu tento explicar isso: http://www.seducaotecnologica.com.br/ruby-eigenclasses-metaclasses/

  7. Sergio Oliveira Jr. 14/03/2010 at 08:37 #

    Resolvi fazer um screencast para tentar desmistificar a diferença entre Metaclass e Eigenclass. Deixão umas questões em aberto tb pois o tema é controverso: http://www.seducaotecnologica.com.br/desmistificando-eigenclass-metaclass-ruby/

  8. Stefano Diem Benatti 02/06/2010 at 20:17 #

    Na verdade o Matz aceitou o nome singleton_class, que será um shortcut para class << self; self; end, como pode ser visto em http://redmine.ruby-lang.org/issues/show/1082, então a nomenclatura deverá se estabilizar em breve.

  9. Rox 01/07/2011 at 13:45 #

    Lembrando que podemos encontrar outras nomenclaturas para as Eigenclasses, além das já citadas, como :

    Ghost Class, Uniclass e Metaclass

Deixe uma resposta