Thursday, February 16, 2012

Methods as first-class objects in Ruby, howto

Recently, I became interested in the general concept of methods as first-class objects in Ruby.

Often, this is useful because they delay their execution, beyond argument-list evaluation time.

Procs and lambdas exist, but I noticed that lambdas are slower than ordinary methods.

The following is a scheme for simply using modules as first-class method objects.

Here are some methods to pass around, using the scheme:

module Double def self.call(x) x*2 end end
module Triple def self.call(x) x*3 end end
double, triple = Double, Triple

You can invoke them in your Ruby code with a syntax similar to that of lambdas:

p triple.call 'a' #=> "aaa"

If you care about lambda's bracket syntax, you have to add:

module Triple instance_eval{alias :[] :call} end
p triple['a'] #=> "aaa"

Here's how to use this module scheme to pass methods as first-class objects:

double=Double
def using_module(m) m.call(1) end
using_module(double)

Some testing:

methods=[Double,Triple]
def take_both(source,m) m.call(source) end
a=[[1],'a'].product(methods).map{|source,method| take_both source, method}
p a #=> [[1, 1], [1, 1, 1], "aa", "aaa"]

Here are some of the alternatives:

module Methods def self.double(x) x*2 end end
def using_symbol(m) Methods.send(m,1) end
using_symbol(:double)

double=lambda{|x| x*2}
def using_lambda(m) m.call(1) end
using_lambda(double)

I tested the speed of the various alternatives (in the 1.9.3, 1.9.2 and 1.8.7 versions of Ruby).

The result? In 1.9.3, the scheme is about fifteen percent faster than passing symbols, which in turn is about twenty percent faster than lambdas.

Copyright (c) 2012 Mark D. Blackwell.