Functional witness protection

November 12, 2008 Adam Milligan

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

Biography

Previous
Tracker: the iPod of project management software?
Tracker: the iPod of project management software?

Nivi, from Venture Hacks, wrote a great article about why Pivotal Tracker is great for startups. It's worth...

Next
Give up the func
Give up the func

I've been a C++ developer ever since I discovered the language in the early 90's and I realized that my bel...