Subsecciones

Símbolos, Métodos y Procs

to_proc

  1. El método method recibe un Symbol que sea el nombre de un método y devuelve un objeto Method
  2. Ruby 1.9 añade el método to_proc a la clase Symbol
  3. Este método to_proc permite que un símbolo prefijado con & pueda pasarse como bloque a un iterador

# Increment an array of integers with the Fixnum.succ method
[1,2,3].map(&:succ)  # => [2,3,4]
que es equivalente a:
[1,2,3].map {|n| n.succ }
El método to_proc podría implementarse así:
class Symbol
  def to_proc
    lambda {|receiver, *args| receiver.send(self, *args)}
  end
end

También podríamos implementar to_proc usando el método methodde la clase Object:

class Symbol
  def to_proc
    lambda {|receiver, *args| receiver.method(self)[*args]}
  end
end

Accediendo a una Clase como una Colección de Métodos

En el siguiente ejemplo, definiremos [] en la clase Module para que podamos escribir:
     String[:reverse].bind("hello").call  # olleh
Para ello basta con hacer un alias [] instance_method
[~/srcLPP/rubytesting/TheRubyProgrammingLanguage/Chapter6MethodsProcsLambdasAndClosures]$ cat -n method_index1.rb 
     1  class Module
     2    alias [] instance_method
     3  end
     4  
     5  puts String[:reverse].bind("hello").call  # olleh
El método retornado por instance_method es un UnboundMethod. Puesto que el método retornado no tiene Binding, es necesario llamar al método bind sobre un objeto para ligarlo.

Es posible llevar esta analogía, usando el UnboundMethod a su vez como hash usando este alias:

     7  class UnboundMethod
     8    alias [] bind
     9  end
    10  
    11  puts String[:reverse]["hello"][]         # olleh

Resulta tentador definir también el operador asignación a elementos de una colección []= para definir métodos:

[~/srcLPP/rubytesting/TheRubyProgrammingLanguage/Chapter6MethodsProcsLambdasAndClosures]$ cat -n method_index.rb 
     1  class Module
     2  
     3    def []=(symbol, code)
     4      define_method(symbol, code)
     5    end
     6  
     7  end
     8  
     9  String[:chuchu] = lambda { |x| puts "Chuchu#{x}!"}
    10  
    11  "hello".chuchu("Cham") # ChuchuCham!
El método define_method es un método privado de Module:
ruby-1.9.2-head :009 > Module.private_methods.select { |x| x =~ /define/ }
 => [:method_undefined, :define_method, :singleton_method_undefined]

En la línea 4 self es el objeto módulo sobre el que estamos definiendo el método (por ejemplo String en la llamada de la línea 9: recuerde que toda clase es un módulo).

Con esta definición de []= podemos escribir:

Enumerable[:average] = lambda do
  sum, n = 0.0, 0
  self.each {|x| sum += x; n += 1 }
  if n == 0
    nil
  else
    sum/n
  end
end

Viendo los (Nombres de) Métodos como Colecciones de Objetos

Visto lo anterior surge la idea de hacer algo similar para los métodos singleton de los objetos. La primera idea sería definir [] y []= en la clase Object. El problema es que muchísimas clases redefinen [] y []= y, por tanto, nuestra definición se perdería.

En vez de eso podemos hacerlo al revés: ver a los nombres de los métodos como colecciones y a los objetos como índices. No queda tan natural, pero tiene la ventaja de que la clase Symbol no es tan heredada como la clase Object.

Para hacerlo abrimos la clase Symbol y definimos ahí los operadores:

[~/Chapter6MethodsProcsLambdasAndClosures]$ cat -n singleton_index.rb 
     1  class Symbol
     2    def [](obj)
     3      obj.method(self)
     4    end
     5  
     6    def []=(obj,f)
     7      sym = self
     8      eigenclass = class << obj; self end
     9      eigenclass.instance_eval do
    10        define_method(sym, f)
    11      end
    12    end
    13  end
    14  
    15  puts :length["Jane"][]  # 4
    16  
    17  x = "hello"
    18  :tutu[x] = lambda { |z| puts "#{x} #{z}"}
    19  x.tutu("Jane") # hello Jane
end
Repase la sección 14.12 para recordar que para abrir la eigenclass del objeto obj usamos la sintáxis class << obj. Así, una forma de obtener la eigenclass es:
eigenclass = class << o; self; end

Casiano Rodriguez León 2015-06-18