This blog is interested in imperative, functional, procedural, logic-based, and all sorts of ways of thinking about programming. I write mostly about C++, my bread-and-butter.
Recent articles have focussed around functional programming in C++; this is one paradigm C++ programmers often neglect. Many believe that it is not possible or efficient to do. I challenge this assertion by example!

Monday, November 26, 2012

The Importance of Function Objects

Function objects allow for freedoms that free functions do not. They are more compatible with higher order functions, composition, and generic programming. They are easier to use; extensible and convenient. They are often even more efficient.

Huh, GCC can't deduce the type of add. Really, the lexical name, add, refers to a whole set of possible template overloads. It could pick one from the set if we supplied some arguments, but not by the name alone. We can fix this by specifying the type arguments.

int sum = std::accumulate( v.begin(), v.end(), 0, add<int&,int&> );

Why do we specify int& instead of int? If we specify int, then because of the quirks in perfect forwarding, signature is

int add( int&&, int&& )

Since its inputs will be lvalues, this will fail to compile. Instead, it will look like this:

int add( int& &&, int& && )

and the arguments will be forwarded to the addition as int&'s.

At this point, falling back on std::plus<int>() would make the most sense, but what if we don't want to have to specify the type? The proposal n3421 expresses this concern, and would allow for the syntax, std::plus<>(), which would be a function object version of add. Finally, that line would compile, but n3421 isn't standard yet. Why not do this?

With this, we could write a simple for loop to iterate over each element, adding the sums of each vector, but that's the whole point of accumulate! We could use std::transform to build a list of sums and accumulate that, but what's the point? The easiest thing to do is use function composition.

Previously, I have talked about function composition. I defined a type, Composition<F,G>, which passed its first argument to G and the result of that along with any following arguments to F.

std::accumulate expects a function where the first value is the accumulator (the sum) and the second value comes from the sequence (in this case: another std::vector). We want the sum of the vector, and then to add the results together. One could fairly trivially write a function that does this explicitly, but that will have to be done for every similar use-case. Patterns of composition can be abstracted away.

Much nicer, no? Imagine more complex examples involving passing higher-order functions to higher-order functions. Without using such techniques, higher order functional programming in C++ would be daunting. Manually deducing types is difficult and error prone--sometime impossible due to implementation details, but the compiler can do it instead, if given the chance. Because of this, techniques in C++ that would otherwise be thought of as impossible become easy.

Finer Control.

std::get<N> is a nice function for working with pairs and tuples, but doesn't work on normal containers. Lets define a function object for that!

We typically think of functions as static, unchanging things, but Selector is dynamic. It could be used, for example, as a function returning the current player in a game by keeping its member, i, always up-to-date.

For another example, consider a function object, Printer, that prints its arguments to some file. We might write that like this: