The tailconst problem (and suggestions for solution)

Walter Bright newshound1 at digitalmars.com
Fri Dec 7 03:00:32 PST 2007


Janice Caron wrote:
> Once I realised that my ampersand idea wouldn't work, because it would
> set up a contradiction between the requirements of generic
> programming, and the requirement of being able to wrap anything in a
> struct, my next thought was a new keyword: "tailconst". (Actually, two
> new keywords, because you'd also need "tailinvariant"). With this
> keyword,
> 
>     class C {}
>     tailconst(C) c;
> 
> would mean "c is a mutable ref to const data".

Regardless of the technical merits of tailconst and tailinvariant, I 
suspect that going from 2 kinds of const to 4 kinds will produce a 
revolt and a general sense that D has miserably failed at a 
comprehensible const design.

This is on top of the problem that I have never been able to find the 
words to explain in a straightforward manner what tailconst even means.

> Of course that does
> then beg the question, what does tailconst(T) mean for T in general,
> if T is not a class. The answer is:
> 
> (1) from the perspective of a human being writing code, consider it
> undefined. Just don't use it. Except with classes, there is never a
> need to use it all, as everything you want can be expressed more
> clearly with const, and

That doesn't work because of the need to have a consistent system for 
use with generic code.


> (2) from the perpective of the compiler, one can come up with some
> rules to make it all work properly under the hood.

I've never been smart enough to keep all the const rules in my head at 
the same time, which is why the last const design is coming up short. I 
forgot a couple :-( This is possibly why I also have trouble explaining it.


> I did actually come
> up with most of what those rules would need to be (...and I can
> certainly tell you if you want...), but then it occurred to me that
> that must be exactly what Andrei did when he proposed his template
> idea.
> 
> In other words, I suspect that my "tailconst(C)" idea is identical in
> practice to Andrei's "TailConst!(T)" idea, the only difference being
> that I made up new keywords and Andrei didn't. I still think that the
> tailconst keyword idea is /slightly/ preferable to the TailConst!
> template idea, if only because of the implementation of the
> specialization for classes. "tailconst" could give us compiler support
> for the notion of "mutable ref to const data", whereas TailConst!
> might involve a lot of fiddling about.

If it can be done with a template, then I'd prefer the template solution 
because: 1) templates need to be powerful enough to do such things and 
this would add credence to D templates and 2) the language is complex 
enough already and 3) there would have to be a big advantage to putting 
it in the core language and I don't see one.


> The third possibility is of course, doing without it altogether.
> 
> That's easy to do in principle: If you want a mutable reference, use a pointer!
> 
>     class C {}
>     const(C)* c;
> 
> ...or arrays
> 
>     class C {}
>     const(C)*[] a;
> 
> There are two problems with this, however. Neither of the following
> two lines will compile:
> 
>     a[3] = new C;
>     auto x = a[3],member;
> 
> The first one won't compile, because the compiler won't implicitly
> convert a reference-to-class-data into a
> pointer-to-reference-to-class-data (and with good reason!). The second
> one won't compile because the type of a[3] is
> pointer-to-reference-to-class-data, not reference-to-class-data, and
> the dot operator will only dereference once.
> 
> The only way I can think of getting around this is to allow two new
> rules (one of which I've actually asked for before). The first is:
> 
> (1) Make it possible to implicitly convert a reference-to-class-data
> into a pointer-to-reference-to-class-data. The implementation of this
> trick must first copy the reference onto the heap, so that the pointer
> points into the heap, not into the stack.

I'm uncomfortable with solutions that imply heap allocation in a 
non-obvious manner.

> (2) (and this is the one I've asked for before) allow dereferencing to
> be recursive, so that p.member will work even if p is a
> pointer-to-pointer-to-pointer-to-pointer-to-data.

The problem with this is the C problem with confusing * and [] for 
pointers and arrays. Such ambiguities work well initially, but as the 
language grows more and more problems result from it.

> I think those two rules will let us work with const(C)*[], and hence
> do away with the need for tailconst altogether.

I'd like to accumulate more experience with const to see if there is a 
pressing need to solve this problem, otherwise we risk throwing in 
complex solutions for non-issues.



More information about the Digitalmars-d mailing list