Give up the func

November 11, 2008 Adam Milligan

I’ve been a C++ developer ever since I discovered the language in the early 90’s and I realized that my beloved Pascal had nothing on objects. I’ve spent plenty of time working with other languages, of course, and over the past year or so I’ve written almost exclusively Ruby. But, one thing I’ve missed about C++ is the ease with which you can make objects act like functions.

In case your C++ is a little rusty, here’s an example:

class Fibonacci {
public:
  Fibonacci(): n1_(1), n2_(1) {}

  int operator()() {
    int result = n1_;
    n1_ = n2_;
    n2_ = result + n2_;

    return result;
  }

private:
  int n1_, n2_;
};

What you’re looking at there is an overload of the function call operator. No, I’m not kidding; that will compile and run. Instances of the Fibonacci class are called function objects, or functors.

Now, you’re wondering to yourself why anyone would care. The answer is, this function can now carry state around with it:

Fibonacci fibonacci;
fibonacci();  // 1
fibonacci();  // 1
fibonacci();  // 2
Fibonacci()();  // 1

vector<int> v(5);
generate(v.begin(), v.end(), Fibonnaci());  // [1, 1, 2, 3, 5]
generate(v.begin(), v.end(), fibonnaci);  // [3, 5, 8, 13, 21]

This may not seem particularly compelling for generating Fibonnaci numbers, but consider generators that may carry more complex state, or references to state owned by other objects. Consumers can also set initial state, such as a seed value for a random number generator, via the ctor.

Also, consider the generate method above. It expects a third parameter that supports function call semantics with arity of zero. And nothing else. That parameter could be a function pointer (should you desire statelessness and impenetrable syntax), a functor of any type, or anything else that supports operator (). That’s duck-typing, my friends. In a statically-typed language. Dogs and cats sleeping together, and all that.

Again, you cry, why would anyone care? Well, blocks in Ruby carry around the state of the context in which they were created, but sometimes you want more. For instance, if you pass your Proc object around your code may be clearer with the state explicitly encapsulated. The initial state may simply not make sense as local variables when you create the Proc. You may want to save some secondary value that a consumer can query the functor for (how many Fibonacci numbers has this generator generated). Or, perhaps you want consumers to be able to mutate the state in some way.

In any case, this functor approach wacked me over the head recently while I was looking at some code that used the Rails Symbol#to_proc. We all know that Rails adds voodoo to symbols so that

User.find(:all).collect(&:name)

is equivalent to

User.find(:all).collect { |u| u.name }

And, we all know that this works because the & operator, when applied to an object in a parameter list, will implicitly call #to_proc on that object and then convert the result to a block. This is vanilla Ruby functionality, Rails just adds #to_proc to the Symbol class.

So, duh. Check it out:

class Fibonacci
  def initialize
    @n1 = @n2 = 1
  end

  def to_proc
    @proc ||= Proc.new do
      result = @n1
      @n1, @n2 = @n2, @n1 + @n2
      result
    end
  end
end

fibonacci = Fibonacci.new
fibonacci.call  # 1
fibonacci.call  # 1
fibonacci.call  # 2
Fibonacci.new.call  # 1

(1..5).collect(&Fibonacci.new)  # [1, 1, 2, 3, 5]
(1..5).collect(&fibonacci)  # [3, 5, 8, 13, 21]

Voilà, a functor. I’d love to hear from anyone who has used this technique to do something really cool.

About the Author

Biography

Previous
Functional witness protection
Functional witness protection

I wrote a bit about function objects here. However, if you don't buy that the persistent state of function...

Next
HAML
HAML

Felix M. and Aaron Peckham talk about HAML.