On Sunday, March 26, 2017 18:31:52 Jerry via Digitalmars-d wrote:
> On Sunday, 26 March 2017 at 15:29:02 UTC, Jonathan M Davis wrote:
> > Personally, I don't think that the fact that you can't use
> > const for head-const in D is really a loss, since it's almost
> > never what you want. Tail-const is _way_ more useful. But it is
> > true that by making D's const fully transitive, there are
> > variations of constness that C++ can do that D can't. immutable
> > pretty much forces that though, and it does simplify the
> > language.
>
> There are quite a few things wrong with const, it's so bad phobos
> isn't even const-correct when it should be. In cmp() for example,
> if you pass a lambda that takes the parameters by reference. You
> will be modifying temporary values that cmp() created on the
> stack. These should be const, but then what if it is a pointer?
> It is a different situation and you don't really want const cause
> you are going to be modifying the correct struct (the one pointed
> to). It is just easier to not use const in D cause it makes your
> code more difficult to actually use. That's exactly what Phobos
> does, it ignores const for the most part because it is easier not
> to use it.

There are significant pros and cons when comparing C++ and D's const, but
they usually relate to the ability to get around const in C++ and the lack
of ability to do so in D. The ability to create a const pointer to a mutable
element is occasionally something that someone wants to do, but in my
experience, it's pretty useless. I'm not sure that I've ever used it in C++
(and I've programmed professinally primarily in C++), and when I was
learning Java and found out that that's what Java's final did, I decided
that all it was good for was constants of built-in types, and I think that
that's mostly what I've seen other folks do with it. Having a
pointer/reference that can't change while what it points to can just isn't
very useful. It's having a mutable pointer to const that's useful.

Now, as for const in D in general and how it compares to C++, that's a
rather difficult question and highly debatable (and it's been debated here
on many occasions). C++'s const is little more than documentation. It
prevents accidental mutation, but there are so many ways around it that it's
borderline meaningless. It only works as well as it does, because it's a
convention that programmers _usually_ hold to. But it gurantees almost
nothing. For instance, it's perfectly possible to have a class where all of
its members are const but where all of them mutate the state of the object.
The fact that that doesn't normally happen is simply because fortunately,
programmers normally choose to behave themselves with mutable and casting
away const. But they don't have to, and the compiler doesn't enforce it.

D's const, on the other hand, has strong guarantees about something that's
const never being mutated unless something elsewhere in the code has access
to a mutable reference and changes the data that way. The const reference is
never able to change the data without violating the type system. So, D's
const actually means something. It provides real guarantees. But the reality
of the matter is that that's so restrictive that pretty quickly you simply
can't use const. There are too many idioms that require backdoors to const -
mutexes, memory management, lazy initializaiton, etc.

So, the end result is that in C++, you don't have much in the way of
guarantees, but you're able to use what you do have all over the place.
Accidental mutation is prevented and programmer intention is conveyed, but
not much of anything is really guaranteed. Whereas with D, you have the
strong guarantees, and you can use const in certain circumstances, but
you're quickly forced to use it in only very restricted circumstances -
especially if user-defined types or templates are involved. Too much simply
doesn't work with true const. What most code really needs is logical const,
and the compiler can't guarantee that.

As to whether C++ or D did a better job with const, I really don't know. I
find C++'s const to be pretty terrible from the standpoint of what it really
protects, and I only recall one time in my entire programming career that
it's actually caught a bug for me, but it's still nice to be able to
indicate that a function doesn't mutate its arguments, even if it's not
actually guaranteed, since it's usually true. The fact that D's const
provides the stronger guarantees is fantastic, but it's also so restrictive
that it borders on useless. So, ultimately, I'm not very happy with either
solution, and I don't know what the best one would be.

Arguably, immutable is what's truly useful - though obviously, code has to
be written with it in mind, because a lot of code won't work with it without
that - but for it to do its job, D's const pretty much has to be the way it
is. Even if we wanted C++'s const in D, we couldn't have it unless it didn't
interoperate with immutable.

And as for the C++ concept of "const-correctness", I think that the end
result of all of this is that it really doesn't apply to D. When code is
const-correct, that essentially means that it will compile with const and
that it's theoretically, "logically" const - _not_ that it's actually const.
It's about convention and conveying the intention of the programmer, not
actually guaranteeing anything. And D's const is all about guarantees. For
better or worse, I think anyone thinking about const-correctness with D code
is almost certainly thinking about const the wrong way in D.

And yes, the end result of all of this is that templated code can almost
never use const. So, a _lot_ of Phobos doesn't use it. And on some level,
that sucks, but I'm not sure that we'd really be any better off with C++'s
const either. There are pros and cons both ways.

- Jonathan M Davis

Reply via email to