Improvement vs Consistency

By Michael Park

The lambda expression is one of my favorite C++11 features next to variadic templates. I won’t talk about the lambda expressions in particular in this post, but I want to talk about the mutable keyword in the context of lambda expressions.

const by Default

C++’s lambdas are technically closures, since it has the ability to capture (save) the surrounding context. Similar to how we need to decide whether to pass function arguments by value or by reference, we need to decide whether we want capture by value or by reference. If we capture by value, the saved context is now part of the lambda’s state and we need to decide whether the operator() is allowed to modify it or not. Let’s see an example:

int n = 42;
auto good = [n]() mutable { n = 45; };  // ok, mutable lambda.
auto bad = [n]() { n = 45; };  // error: cannot assign to a variable captured by
                               //        copy in a non-mutable lambda

As you can see, the default for lambdas is to be non-mutable, or const. That is, a lambda needs the mutable keyword to be able to modify its internal state.

This is New…

Throughout C++, the language consistently chooses mutable by default. For example, variable declarations (local, member, etc) and member function declarations need const tacked onto them in order to make them non-mutable. The fact that lambdas have the opposite default behavior is new to the language.

Partial Improvement

The improvement here is that const by default is actually the better choice. I suspect that this is a lesson learned from functional languages such as ML and OCaml which provide immutable values by default. As a result, we get referential transparency more often which helps programmers and compilers to reason about a program. Andrej Bauer also talks about this topic in his blog post, On programming language design, in which he says:

We should design the language so that the default case is an immutable value. If the programmer wants a mutable value, he should say so explicitly.

We also don’t want to be restrained by the decisions made in the past, of course we want to keep on improving… but we still need backwards-compatibility.

But… Consistency…

I have a big thing for consistency. I think keeping consistency in a language makes it so that there’s less to remember and therefore easier to learn and use. This is no different in programming languages than human languages such as… English. English has a ridiculous number of special cases to the point where one has to learn the language almost on a phrase-by-phrase basis. Learning a pattern and applying it consistently will certainly lead you to sound like a foreigner in no time.

I think the language becomes unnecessarily complicated and harder to teach when we introduce special cases like these.

So which is better?

I’ve argued both sides against myself and there doesn’t seem to be a definitive answer. I want to be able to make improvements going forward, but I’m tired of saying and also hearing “They’re the same, pretty much. They’re almost always the same. Um, one case where it’s different…”

In this specific case, I think I would’ve preferred to keep consistency. The committee members voted otherwise however, so who am I to disagree.