Head Const

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Thu Feb 18 07:32:25 PST 2016


On Thursday, 18 February 2016 at 14:58:00 UTC, ixid wrote:
> On Thursday, 18 February 2016 at 11:57:59 UTC, Jonathan M Davis 
> wrote:
>>The problem that folks frequently want to be able to solve that 
>>they simply cannot solve >with D's const (and headconst 
>>wouldn't help) is that they want to be able to pass an object
>>>to a function that takes it as const or return a const object
>>from a const member function >and have stuff like reference 
>>counting, caching, mutexes, etc. work - stuff that has to be
>>>part of the object (at least in pure code) but which isn't
>>part of its logical state and >cannot be const.
>
> Is it not possible to have two objects, the data and the 
> information about the data? It seems like a mistake to try to 
> treat metadata as the data. I just ask out of interest as I 
> lack the experience to have a meaningful view, the category 
> confusion between data and metadata seems like a path to 
> excessive complexity.

To some extent yes, to some extent no. Lets' say that we have a 
RefCounted wrapper which does reference counting, e.g.

     RefCounted!Foo foo;

The ref count is in RefCounted, not Foo, so you can do something 
like

     RefCounted!(const Foo) foo;

and the ref-count will still work, but if you do

     const RefCounted!Foo foo;

or

     const RefCounted!(const Foo) foo;

RefCounted can't actually mutate its ref-count anymore, even if 
it's copied, because the copy would have a const reference to the 
ref-count, because the copy was made from a const RefCounted!Foo 
and not a RefCounted!Foo. So, it really only helps you for one 
level of const. The only way to get around the problem would be 
if the ref-count were in a global or static variable somewhere, 
in which case, it can't be used in pure functions, which is 
definitely negative.

Similarly, what do you do with cached data? In C++, cached data 
would normally go in a mutable member variable so that the 
calculation could be saved, even if a const function called it - 
but since the calculation was cached and wouldn't change, the 
const function would still be logically const. The only way to do 
that in D is to either have the cache set only in mutable 
functions or to make the function not pure and put the data out 
in a global or static variable somewhere, which again, is 
definitely bad.

Similarly, mutexes really should be associated with the data that 
they're protecting, which often means being passed around with 
the object that contains that data. And that won't work with 
const. You'd once again be forced to put the mutex it a global or 
static variable and forego pure. synchronized works around this 
for some cases by having the type system put the data somewhere 
that's not treated as const, but it's a special case added by the 
language. It wouldn't work at all with explicit mutexes that 
didn't have special language support like that.

In C++, these sorts of problems are solved by marking the 
ref-count, cached variable, mutex, etc. as mutable. Then that 
variable can be mutated in order to do what it needs to do even 
though it's being used in a context where the object that it's in 
is const. Obviously, that can be abused by marking everything as 
mutable and effectively making const meaningless (which is part 
of why Walter isn't a big fan of C++'s const), but in practice, 
that doesn't normally happen, and without mutable or casting away 
const and mutating (neither of which are valid D), you just can't 
do those things with const - especially when you add pure into 
the mix.

- Jonathan M Davis


More information about the Digitalmars-d mailing list