Stepping back and looking at constness from another angle.

David B. Held dheld at codelogicconsulting.com
Mon Jun 4 22:05:13 PDT 2007


Let me point out a few guiding principles that are worth considering:

1) The more code you break, the more benefit you must offer

2) Orthogonality is good; exceptions and corner cases bad

While the idea of making const the default sounds appealing, why limit 
it to function parameters?  Why not treat variables as const-by-default 
also?  Very functional indeed.  Unfortunately, not very D-like.  D could 
have builtin lists, just like functional languages.  Instead, it has 
arrays.  Guess why?  Arrays are smaller and faster.  D chooses 
performance.  Those arrays could be immutable by default, but they 
aren't.  Why not?  Try to time the performance of quicksort in Haskell, 
and compare it to C's qsort(), and you will soon understand the power of 
mutable arrays.  Haskell's version certainly looks better, but C's burns 
the pants off all takers, because it's about as fast as hand-coded, 
fully optimized, no-holds-barred assembly.

D is a multi-paradigm language, like C++, but let's not forget that 
"imperative" is a paradigm, and that machine language itself is 
imperative.  And since D aims to let you get as close to the machine as 
possible, it makes sense that the default for *variables* is mutability.

A corollary to principle 2) above is:

3) Function parameters should have the same syntax as local declarations

Why?  Well, why not?  Why should users have to think hard about whether 
a particular syntax is only legal for parameters or not?  While certain 
qualifiers like 'in' and 'out' must necessarily be exceptions, they idea 
is that they provide sufficient convenience to justify their existence. 
  Beyond that, you should be able to look at a function parameter list 
and say: "Each of those parameter declarations could be pulled out into 
a local declaration".

If we agree that is a powerful principle that makes the language easier 
to understand, then we see why it is not acceptable for function 
arguments to default to const while locals do not.  And if we agree that 
D is an imperative language before all else, because it is a *systems* 
language, then we see why it is not acceptable for locals to default to 
const.  This leads us to conclude that mutability should indeed be the 
default, and that constness should be indicated explicitly.  However, I 
think 'in' as a synonym for constness is a very nice compromise that 
gives just about the shortest possible abbreviation for 'const' that you 
could imagine (short of something silly like type sigils).

One person mentioned that Perl doesn't have 'const'.  In fact, that is 
only half-true.  There is a Perl pragma called 'constant' which is used 
like thus:

use constant PI => 3.14159;

Unfortunately, being a pragma, the symbol PI does not interpolate like a 
scalar, making certain uses of it very awkward.  Larry recognized both 
that this was unacceptable and that constants are indeed very useful, 
and decided to give Perl 6 first-class constants.

Also, Andrei likes to make much of the fact that Perl, like Java, has 
immutable strings.  Who would have guessed?  Perl is pretty much the 
poster child of string manipulation and obscure do-anything code 
semantics, so unless you went looking for it, very few people would 
guess that strings in Perl are actually immutable, but they are.  Why is 
that?  Well, it's because making strings CONST makes it very easy to 
guarantee that there is no improper aliasing mixed with string mutation.

Java programmers that need mutable strings use StringBuilder, and then 
convert to a String when they're done.  It's such a powerful idiom that 
the xxxBuilder pattern is quite popular in the Java standard library 
now.  How could D get immutable strings?  Well, we could try it with 
const, but if you look closely, 'const' doesn't mean "this array can't 
change".  It means: "You can't change this array through this const 
reference, but somebody else can."  That is, 'const' isn't strong enough 
to get you immutable strings.  For that, you need something stronger. 
You need 'invariant'.  Only invariant guarantees that nobody holds a 
mutable reference to your object, and thus, you are free to alias the 
object all you like, quite safely.  D, being a *systems language*, 
concerned with *performance*, will take any performance-enhancing 
substance available, and 'invariant' is one of the best drugs on the 
market (eat your heart out, Floyd).

So could we live with 'invariant' and dump 'const'?  Well, we could, but 
it wouldn't result in a very useful language.  Because then we wouldn't 
be able to say: "I want the implementor of this function to have a 
read-only view on my precious mutable data which I need to be able to 
update in place."  The only way you could do that is to make a copy, 
which defeats the whole point of 'invariant'.  Are things starting to 
become a little more clear?

So if we agree that the Functional Paradigm is A Good Thing, and part of 
why it's a good thing is Purity, then it follows that D can gain some of 
the benefits of FP by implementing limited purity, which is what 
const/invariant are.  Why is purity a good thing?  It's very simple. 
Functions, in the mathematical sense of a map from A to B, are fairly 
easy to understand.  State machines, on the other hand, are much much 
harder to understand.  Adding variables to a state machine typically 
increases the number of states geometrically, and the number of state 
transitions exponentially.  Adding constants, on the other hand, does 
not increase the number of states, nor the number of transitions, which 
means that you literally get an exponential increase in simplicity by 
making as many things immutable as possible.  And that's why 'const' in 
general is A Good Thing(TM).

Of course, the cost of all this is some additional syntactic burden. 
But consider the opposite case: pure functional languages trying to 
implement mutable state.  In our case, we only need to sprinkle a 
keyword here and there.  For Haskell, you need to *define a Monad*.  In 
case you didn't know, a Monad is a structure with three distinct 
operations which are fairly non-trivial for a novice to define.  On the 
other hand, most novices can add 'const' to a declaration without any 
assistance whatsoever.  This is most likely why even most functional 
languages are not pure.

If you think that you are willing to take the plunge and make all your 
programs const by default, consider all the libraries that D can and 
does link to, and consider the effect of const-by-default on that 
codebase.  Even if you are willing to use a Haskell-like discipline 
yourself, most D code is not, and would take a huge effort to fix up.

One final observation is this: the benefit of const is something of an 
emergent property that is sensitive to scale.  That is, for small 
programs, the cost outweighs the benefit.  But as a program grows from a 
few modules to several libraries and tens or hundreds of modules with 
many thousands of lines of code written by teams of programmers, the 
benefit of const pays back its cost through every possible interaction 
of programmers.  Why is that?  It's because 'const' is both 
documentation and a promise.  It tells another programmer what to 
expect, and it promises to follow through on that created expectation. 
Since the number of possible interactions among programmers is factorial 
(super-exponential!) in the number of coders, the benefit of const soon 
becomes apparent with even a modestly-sized team.

If you have reservations about const on your personal project, try to 
imagine writing a very large project with a group and ask yourself how 
much you trust the other programmers (imagine the group stepped out of 
Dilbert, and not your favorite startup).  While inferring 'const' is 
certainly possible (and several papers have been written about how to do 
it), it neglects the documentation aspect of 'const'--the contractual 
aspect of it.  This is something that only becomes apparent at large 
scales, but once you reach those scales, it becomes very visible very 
quickly.

If there is some magic const-bullet that would make everyone happy, I'm 
sure Walter is the first person in the D community that wants to know 
about it.  Unfortunately, this is a very hard problem, and solutions to 
hard problems always involve some kind of compromise.  Of course, the 
problem with compromises is that they are guaranteed to piss off 
everyone at least a little bit (look at immigration reform for a good 
example).  That doesn't mean it isn't a good compromise; on the 
contrary, it probably means it is.

Dave



More information about the Digitalmars-d mailing list