huge volumes of pointless syntax
13 Jun 2006It’s high time for the next installment in my irregular series of C++ rants. This time I’m going to talk about expressiveness (or rather, C++’s lack thereof)
Expressiveness indicates a capability to accurately and briefly express something. A human being is considered to be expressive if they are able to speak in such a way that someone listening to them can quickly understand the person’s message. A computer language’s expressiveness is a measure of how quickly a programmer can write code that is unambiguous and a correct reflection of the design in the programmer’s mind.
Let’s illustrate this with an example. Imagine we’ve got a class with an instance variable containing an array (or list, or vector, or what have you) of some other type. We want to give our class a method that takes another array, iterates through all the objects in it, and adds them to its own array. Obviously this is a contrived example, since nearly all container classes in all languages must include functionality for adding the contents of one array to another, but the point here is to demonstrate how a simple iteration is done.
Let’s start off with the C++ version, something like this:
class ValueContainer
{
// a vector containing Value objects
std::vector<boost::shared_ptr<Value> > m_values;
void addValues(std::vector<boost::shared_ptr<Value> >values)
{
// an iterator pointing at the first value
std::vector<boost::shared_ptr<Value> >::const_iterator it = values.begin();
// an iterator pointing at the special “end” value indicating we’ve reached the end
std::vector<boost::shared_ptr<Value> >::const_iterator endIt = values.end();
// step through the entire array
for (; it != endIt; ++it)
{
// add the value to our array
m_values.push_back(*it);
}
}
};
Some of you may be unfamiliar with the
boost::shared_ptr
stuff in there. The boost project is a sort of proving ground for classes that are probably destined for the standard c++ library in the future, and shared pointers are a very valuable concept that make c++ development a little more do-able, by helping you manage object ownership.Anyway, that’s quite a pile of code, isn’t it? All we’re doing is walking through a list of things!
Some may balk at this, saying that the boost shared pointers are overkill here. OK, fine, let’s take those away, and take away the comments, and stuff all we can into the
for
, getting this down as tight as we can:
class ValueContainer
{
std::vector<Value> m_values;
void addValues(std::vector<Value> values)
{
for (std::vector<Value >::const_iterator it = values.begin(); it != values.end(); ++it)
{
m_values.push_back(*it);
}
}
};
Now let’s see how a similar class might look in python:
class ValueContainer:
def __init__(self):
self.values = []
def addValues(self, values):
for v in values:
self.values.append(v)
Unlike C++, Python has no free-standing declarations of instance variables—they’re defined only as they’re referenced inside of methods, and the best practice is typically to provide an initial definition inside the initilializer method
__init__
as I’ve done here.There are several nice things about Python that this highlights:
- the syntactic indicator that we’re dealing with an instance variable by always prepending
self.
- the more sensible method names in Python’s array class compared to C++’s std::vector class (
append
vspush_back
) - the
values
parameter isn’t statically typed, so it doesn’t need to actually be a standard python array, but can be any class that implements the methods required for iteration
but the big win I want to point out here is really the difference in the for
constructs.
Look at the mass of ugliness in the C++
for
construct. To start off with, you’re forced to declare a variable with a bizarre iterator
type. Why should I have to care about that? I’m not using it in any way; that type is completely irrelevant to my code. Later, to iterate I have to use it++
, which may not be pointer arithmetic, but at least looks like pointer arithmetic, and that is just so 1985.Then, take a look at the simplicity of the Python
for
construct. The designers of Python built iteration right into the language! You simply give for
the name of the variable that should be used to hold a value from the array, and the array itself, and off it goes. Behind the scenes, iteration occurs through well-defined methods, and if you’re making your own collection class you can define your own iteration scheme, but all of that is completely invisible to a user of the class.IMHO, this sort of thing is really one of the biggest wins for languages like Python. You get to leave out all sorts of meaningless busy-work, since the language includes useful mechanisms that C-based languages don’t, and you are left with the job of programming the actual functionality you’re after.