www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - constancy and template parameters in D2.009+

reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 I certainly hope you will look at the thread "Constancy lost
 when compiling template mixin", and respond in some way - even if only
 to tell me I've made some obvious and stupid error - because the issue
 raised there is one that I /do/ need solving.

I wouldn't worry about that right now, as the const stuff is getting revised again.

Cool. I'll accept your word on that. But one thing I would like you to think about is that templates need the ability to determine the constancy of their parameters. That is, Array!(int) sometimes (but not always) needs to be a different instantiation from Array!(const(int)), because sometimes they need to behave differently. In D2.007, they were indeed different instantiations. In D2.008, they resulted in the same instantiation, unless you declared your template differently. That is, instead of writing class Array(T) you now had to write class Array(T:T) if you wanted int and const(int) to generate different instantiations. This isn't immediately obvious - in fact, I'm not even sure how to read it! ("T where T is a type of T", perhaps?). To make matters worse, as I demonstrated in the thread "Constancy lost when compiling template mixin", the (T:T) syntax doesn't actually work in all circumstances. In D2.008, it fails in my example (where I use a (T:T)-type template as a mixin). From your answer, I guess I have to assume this is a compiler bug, and I trust that the bug will disappear in D2.009. A related problem is that of /removing/ constancy. Right now (in D2.008), I use alias typeof to remove constancy. That is: class Array(T:T) { alias typeof(T) U; ... yeilds a type U equivalent to T without the const - exactly as if I had declared class Array(U) { ... Again, my complaint is that the syntax is not obvious. Why should "alias typeof(T)" be the way to remove constness? Sure, it /works/, but as a statement of intent, it's not obvious. In all of this, there is a common theme - SYNTAX. In all of the examples I have described, my complaint is that /the syntax is not obvious/. It's not obvious that (T) should mean "don't regard head constancy as significant"; it's not obvious that (T:T) should mean "do regard head constancy as significant"; and it's not obvious that typeof(T) should mean "remove constancy". (And in fact, I'm not even sure whether it removes only head constancy or all constancy). So, while you're still working on this, please may I suggest a simple and obvious syntax refinement, which I believe will do exactly the right thing in all circumstances, and be completely readable... RULE ONE: Head constancy is not considered significant in template instantiation. Thus, (exactly as in D2.008), if "class A(T)" is instantiated with "int" and "const(int)", it will result in exactly one instantiation, with T being "int". RULE TWO: Head constancy may be explicitly parameterised with an /additional/ template parameter. For example: class Array(U, T=const(U)) The intent here is that, if instantiated with "int", U and T will both be "int", but if instantiated with "const(int)", U will be "int" and T will be "const(int)". Likewise, if instantiated with "invariant(int)", U will be "int" and T will be "invariant(int)". RULE THREE: There is no rule three. (Or, put another way...), get rid of the silly typeof(U) syntax for removing constancy. It's really not obvious what's going on anyway. In fact, I don't think typeof(U) should even /parse/, if U is a type, not a value! In any case, the point is that RULE TWO already creates two types - U without constancy, and T with, and so the need to explicitly remove it from T to generate U is now redundant. Well, that's it - that's my suggestion for the day. I hope it helps.
Dec 13 2007
next sibling parent reply guslay <guslay gmail.com> writes:
Janice Caron Wrote:

 RULE ONE: Head constancy is not considered significant in template
 instantiation.
 
 Thus, (exactly as in D2.008), if "class A(T)" is instantiated with
 "int" and "const(int)", it will result in exactly one instantiation,
 with T being "int".
 

I don't really understand the motivation behind the current behavior. Can someone explain why stripping away constness is not explicit?
Dec 13 2007
parent reply guslay <guslay gmail.com> writes:
Janice Caron Wrote:

 On 12/13/07, guslay <guslay gmail.com> wrote:
 I don't really understand the motivation behind the current behavior. Can
someone explain why stripping away constness is not explicit?

Most of the time, stripping away head constancy is exactly what you want. Failure to do so results in multiple identical instantiations.

I am not convinced... Identical specialization in code verbatim, maybe, but a.method() and const a.method() are not the same method call! This is apart from the side effect that you lose const protection. Since we are talking about generic code, it seems like a safety issue. Regarding coexistent multiple specializations, assuming they are all the same, i would like to know exactly in what respect this is detrimental. Yes, it increases executable size a bit. Are there are actual measures? Does that impact performance? Also, how often does one instantiate all flavors (there are three) of const qualifications in an application?
 
 But sometimes it matters, which is why Walter allowed the (T:T)
 notation, for when it does. I just think there has to be a better
 syntax for that!

I am all for a mecanism to discard const-qualification in the definition of the interface (as opposed to casting away const from the call site everywhere) if there are benefits. I just hardly see how this should be the default.
Dec 13 2007
parent guslay <guslay gmail.com> writes:
Janice Caron Wrote:

 On 12/13/07, guslay <guslay gmail.com> wrote:
 but a.method() and const a.method() are not the same method call!

No one said it was. You're confusing a function call with a template instantiation. Be careful - that's a very different thing.

I was not clear. What I meant was that you often instantiate a template class parameterized by T to use a variable "a" of type T and call methods on it.
 Consider a function such as:
 
     T max(T)(T x, T y) { return (x > y) ? x : y; }
 
 Do you /really/ need separate instantiations for T==int and
 T==const(int)? Of course not. Walter's choice is reasonable. (It may
 not be perfect, but it's reasonable).

Well if T is a reference to a const object, yes, I want to keep the const.... But I understand what you mean. Sometimes, constness doesn't matter, for instance if I want allocator(T), I'm only interrested in the sizeof T. I think it's a nice innovative feature. However, I'll argue that this optimization is the exception rather than the rule.
 This is apart from the side effect that you lose const protection.


Good to know!
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, guslay <guslay gmail.com> wrote:
 I don't really understand the motivation behind the current behavior. Can
someone explain why stripping away constness is not explicit?

Most of the time, stripping away head constancy is exactly what you want. Failure to do so results in multiple identical instantiations. But sometimes it matters, which is why Walter allowed the (T:T) notation, for when it does. I just think there has to be a better syntax for that!
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, guslay <guslay gmail.com> wrote:
 but a.method() and const a.method() are not the same method call!

No one said it was. You're confusing a function call with a template instantiation. Be careful - that's a very different thing. What we're talking about here is the difference between A!(int) and A!(const(int)). In D2.007 they were distinct by default. Walter says that led to a lot of instantiation bloat, so in D2.008 they were coalesced by default. Consider a function such as: T max(T)(T x, T y) { return (x > y) ? x : y; } Do you /really/ need separate instantiations for T==int and T==const(int)? Of course not. Walter's choice is reasonable. (It may not be perfect, but it's reasonable).
 This is apart from the side effect that you lose const protection.

No you don't. If const correctness is ever violated, it's still a compile error.
 Regarding coexistent multiple specializations, assuming they are all the same,
i would like to know exactly in what respect this is detrimental.

<shrugs!> That's Walter's decision.
 Also, how often does one instantiate all flavors (there are three) of const
qualifications in an application?

I would say rarely. Certainly there are cases when it's important - Array!(int) and Array!(const(int)) need be distinct, for example, so that they can behave like int[] and const(int)[] respectively, but apart from collections, the distinction is probably wasted.
 But sometimes it matters, which is why Walter allowed the (T:T)
 notation, for when it does. I just think there has to be a better
 syntax for that!

I am all for a mecanism to discard const-qualification in the definition of the interface (as opposed to casting away const from the call site everywhere) if there are benefits. I just hardly see how this should be the default.

I honestly don't care - so long as I get the best of both worlds, and a sufficiently non-confusing syntax.
Dec 13 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, guslay <guslay gmail.com> wrote:
 Well if T is a reference to a const object, yes, I want to keep the const....

Then you should be happy with the D2.008 behavior: It's only the headmost layer of constancy which is deemed not to matter. The tail constancy is still considered a discriminator.
Dec 13 2007