year ago, everyone was all but certain that the C++0x standard was just around the corner, and that it would include concepts (see Danny Kalev's earlier interview with Bjarne Stroustrup, the creator of C++, from August 2008). However, In July 2009 the C++ standards committee decided to remove concepts from the C++0x by an unprecedented move. Danny's recent controversial editorial was among the first to report that decision and its possible consequences. Despite vociferous disagreements over the removal of concepts themselves, nearly everyone agrees that the committee's decision left open many questions not only about concepts, but also about the committee's charter, and even the future of C++ itself.
Therefore, Danny has interviewed Bjarne Stroustrup again, this time to capture his thoughts about concepts, their removal, and the impact of that decision, along with his take on other pressing questions that currently concern the entire C++ community.
Danny Kalev (DK): Which conclusions do you draw, if any, from the failure of concepts? How do you feel about this whole affair? Might it dissuade you from proposing innovative features in the future?
Bjarne Stroustrup (BS): You mean from the decision not to ship concepts more or less as is for C++0x? I am not of the opinion that concepts have failed. My position was that we needed only a few weeks to "fix" what in my opinion were serious usability problems. Obviously, a majority of the committee didn't agree with that timescale. But just about everyone I talked to expressed support for the idea of concepts and I had to warn against over-optimism about the timescale to get concepts back once they were removed from the working paper. There is a significant difference between "failure" and "not being ready to become the standard for millions of programmers."
The power needed to propel a pebble across town will barely move a boulder. However, moving the boulder may have huge benefits, whereas nobody cares where the pebble lands (unless it is in your shoe). In other words, making a change to C++, which is large and in major real-world use, is hard but offers the possibility of helping millions of programmers. Thus, I consider trying to improve C++ worthwhile despite the required effort and occasional setbacks.
|Bjarne Stroustrup: I think that concepts were—and will again—become "a big deal."|
How do I feel? Disappointed, but not crushed. Things could have been much worse. In particular, we could have made the seriously flawed "concepts" part of the standard.
DK: The specification of concepts has taken seven years. By contrast, the standardization of the entire STL took about half that time. What is so complicated about concepts?
BS: I count the concepts work as started in 2002; I presented my first design papers in 2003. In 1994 Alex Stepanov presented a complete implementation of the STL to Andrew Koenig and me, but 2002-to-2009 and 1994-to-1998 are not comparable time lines. Alex had been working on the STL from at least 1976.
But to answer your specific question: Concepts are hard to design because they represent a formalization of the C++ type system. Doing concepts right implies a more formal specification of much of C++, and requires reaching a new level of precision in the description of the C++ standard library. In other words, doing concepts right implies clearing up many dark corners of the C++ specification. Doing concepts right also implies using them does not mean significant increases in compile time, decreases in run-time performance, or erecting new barriers to effective generic programming. My worry about concepts as present in the pre-Frankfurt working paper was the last point. Others worried about the first.
I suspect that quite a few readers have only the vaguest idea of what concepts are and should be, so let me give a couple of examples. With concepts, we can declare the four obvious versions of sort() like this:
void sort(C& c);
template<Container C, Comparator Cmp>
void sort(C& c, Cmp cmp);
void sort(Iter p, Iter q);
template<RandomAccessIterator Iter, Comparator Cmp>
void sort(Iter p, Iter q, Cmp cmp);
Most people will find this pretty obvious—and then proceed to wonder why we didn't do that in C++98. After all, we say that sort() takes a pair of random access iterators plus, optionally, a comparison function. Furthermore, most of us would like to have the simpler way of specifying a sort() for a whole container. Here—using concepts to specify what is required for the template arguments—we simply say what we mean. The result of doing so is to allow overloading, and get error messages for bad template arguments right away. For example, if we call sort(lst.begin(),lst.end()) and lst really is a list then the error messages in C++98 are late (link time), and typically obscure. With concepts, they are instant and precise. Note that only declarations are required for type checking and overload resolution; just as for ordinary functions, the definitions (function bodies) can be "elsewhere."
Why is this difficult to implement? This particular example is simple to specify and to implement, but people write templates that depend on really obscure details of types (e.g. the template argument type must have a trivial destructor, the template argument type must be move-constructible, or the template argument type must be a union). They do so because those details are important to them in their application domain. My experience is that there is hardly any aspect of C++'s type system that isn't important to someone. Representing all these possibilities as concepts is a lot of work—and has in fact helped us discover ill-defined details that would eventually have caused problems for someone.
Concepts don't just help template users, they also have a major role in supporting template implementers. Consider:
template<ForwardIterator Iter, class V)
requires Comparable<Iter::value_type, V>
Iter find(Iter first, Iter last, V x)
while (first!=last && *first!=x) first = first+1;
This looks quite plausible. The standard find() does indeed require a pair of forward iterators, and the value does have to be compared to a value of the iterator's value_type. That is, concepts have been used simply to formalize the text in the standard. However, the snag with that code is that a forward iterator doesn't provide +, just ++. Given the code above, the compiler correctly rejects first = first+1. Using only C++98, finding that error would have required testing and/or a clever programmer.
Here, I presented only what I consider uncontroversial. The discussions have been about design issues far more obscure than this.
Someone might insist "But better error messages are not such a big deal; why should I care?" Well, they are a big deal, but only one of the beneficial effects of "concepts." The major effects are on programming style and code quality. Try imagining the days before I added function argument type specification, argument type checking, and overloading to C as part of creating C++. There was no shortage of people who objected to the verbosity of…:
double sqrt(double);// modern function declaration
…as compared to the (then) familiar…:
double sqrt();// pre-prototypes C function declaration
Some worried about compile-time overhead; others about run-time overhead (yes, really; after all, you might get an undesired conversion from int to double), others about complexity (can we really teach average programmers to use function declarations well?), and still others about compatibility (how does "old code" interact with code using function declarations at compile and link time?). Now, even the most die-hard C programmer wonders how people ever managed without function prototypes.
DK: So perhaps the conclusion is that concepts were doomed to fail because they try to fix so many things at once, by transforming C++ into an almost new language? After all, the whole notion of templates is a bit problematic in C++—what the programmer writes or reads is quite different from the actual (unseen) code that the compiler generates and parses when it processes a template instance. In other words, maybe it would be better to leave C++ as-is in spite of the well-known limitations of templates?
BS: No. I don't think that concepts were doomed to fail. Also, I don't think concepts were trying to fix many things or to transform C++ into an almost new language. They were introduced to do one thing: provide direct language support to the by-far dominant use of templates: generic programming. They were intended to allow direct expression of what people already state in comments, in design documents, and in documentation. Every well-designed template is written with a notion of what is required from its arguments. For good code, those requirements are documented (think of the standard's requirements tables). That is, today most templates are designed using an informal notion of concepts.
The reason we need direct language support for concepts is that an informal notion is not good enough: Compilers do not read comments, design documents, or standards and "informal" in the hands of most programmers (I don't except myself) means "imprecise." That is, the lack of formal, language supported concepts is a source of bugs as well as poor error messages. I think the analogy between concepts in template declarations and arguments in function declarations (function prototypes) are more relevant than many like to believe.
I do not think that templates are "a bit problematic." For starters, they are not macros. Programmers should no more worry about the transformations a compiler performs on template code to produce executable code than they do about the transformations compilers do for non-template code to produce executable code. One reason that some people do worry (and that some people do think of templates as macros) is exactly because the requirements on template arguments are informal; they're not checked at the call point. If you worry about such "transformations" you should be very keen on concepts. If you don't, maybe you should write in C without using prototypes.