I wrote a bit about function objects here. However, if you don’t buy that the persistent state of function objects provides something that anonymous functions cannot, how about this: readability. In some cases.
Anonymous functions are boss and cool, and extremely common in idiomatic Ruby. However, in some cases they can get a little… esoteric. Consider:
people.sort do |lhs, rhs|
lhs, rhs = rhs, lhs if ascending?
result = lhs.name <=> rhs.name
if result == 0
result = lhs.date_of_birth <=> rhs.date_of_birth
end
# etc...
end
Sometimes, anonymity isn’t the answer. Consider:
class ByNameAscending
def self.to_proc
Proc.new { |lhs, rhs| rhs.name <=> lhs.name }
end
end
This allows you to write this:
people.sort(&ByNameAscending)
Or, to push the example to the extreme:
class SortOrder
def initialize(direction = :descending)
@direction = direction
end
def by(attribute)
attributes << attribute
self
end
alias_method :and, :by
def to_proc
Proc.new do |lhs, rhs|
lhs, rhs = rhs, lhs if ascending?
return lhs <=> rhs if attributes.empty?
attributes.each do |attribute|
result = lhs.send(attribute) <=> rhs.send(attribute)
return result if result != 0
end
0
end
end
private
def attributes
@attributes ||= []
end
def ascending?
@direction == :ascending
end
end
def ascending; SortOrder.new(:ascending); end
Which gives us:
people.sort(&ascending.by(:name).and(:date_of_birth))
A DSL for generating sort order function objects. It could be useful.
About the Author