January 02, 2008
Walter Bright wrote:
>> Most C++-users I know refuse to even acknowledge that C++ is bad.
> 
> Interestingly, many C++ users I know will not publicly acknowledge the flaws in C++. Off the record, though, they say very different things. People do not want to damage their careers built up around C++. There are some pretty high level C++ experts who have been privately very helpful to me in explaining how to do things right.

That is interesting.  One of the things I've found almost unique about C++, of widespread programming languages, is that its experts are absolutely willing to discuss weaknesses of the language, and not to pretend that it's perfect, or to spend excessive time denigrating other languages (which also have their strengths and weaknesses).  C++ is a very open language, though unfortunately it's too big and complex for anyone to master all of it, and its big and complex enough that many mistakenly think they understand it.

However: saying "C++ is bad" is too simplistic to be useful.
C++ is not beautiful; however, it's immensely useful and powerful.
The same could be said for Perl (which takes it to a whole new
level).  C++ is flawed; so are all other programming languages.
There's no point wasting time debating whether (any) language is
"good" or "bad" -- it's necessary to speak in terms of specific
pros and cons, in specific contexts.

-- James
January 02, 2008
Walter Bright wrote:

> Lars Ivar Igesund wrote:
>> It is hard to try to be involved (in any capacity) with D language evolution when there are silent (or rather non-public) opinions that weighs more than this newsgroup, and I think as people on the newsgroup figures this out, it will be a loss for D.
> 
> It is not that various opinions weigh more. It is more trying to pick the best possible course of action. Note that I do my best to argue the cases based on their merits, not based on votes. Consider that after the feedback in this group, the first two const regimes were scrapped. You guys made a convincing argument that it was confusing, overly complex, and basically sucked.
> 
>> In earlier situations that are similar to this one, it has always looked like the unvocal persons are C++-users, thus we end up in a very C++ inspired language when that often is definately not what we want.
> 
> const in D is very unlike const in C++.
> 
>> Most C++-users I know refuse to even acknowledge that C++ is bad.
> 
> Interestingly, many C++ users I know will not publicly acknowledge the flaws in C++. Off the record, though, they say very different things. People do not want to damage their careers built up around C++. There are some pretty high level C++ experts who have been privately very helpful to me in explaining how to do things right.

I am not really attacking your integrity here, although I guess it sounds like it most of the time. My point is more to the fact that the argumentation that in the end leads to how something is implemented, often is kept (intentionally or not) from the public, leading to the impression that you from time to time make arbitrary decisions.

This wouldn't necessarily be that big a problem if you could convey the actual argumentation to the newsgroup in a convincing manner. For the enum/manifest constant case I dare to say that you have failed to do that as I have yet to see a reply to your argumentation going like this "Ah, you're right!". There's been a few of "Hmm, I guess I'll accept it ...", but no turnarounds.

Again, in this case it is not about functionality, as all seems to agree we want manifest constants (I certainly do), just about looks and whether the scheme will be confusing or not. In the discussion of C++, it is my opinion that although D is improving upon C++, the masses that it would be nice to pull in come from the VM (Java, C#) world and they won't have any understanding for design decisions based on conventions in C++.

-- 
Lars Ivar Igesund
blog at http://larsivi.net
DSource, #d.tango & #D: larsivi
Dancing the Tango
January 02, 2008
"Walter Bright" wrote
> Bill Baxter wrote:
>> What was the fundamental problem with creating some syntax that would let you peel the reference part off a class reference?
>
> const(C)&[] a;
>
> is a sortable array of constant C's. But what is:
>
> const(int)&[] a;

A sortable array of int references.  If references are to be consistent, the meaning of an int reference is that it acts like an int until you assign to another int reference.  If you assign it to another int reference, then type now references the same thing as the new int reference.  If you assign it to another int, that is a compiler error (can't assign a non reference value to a reference just like you can't assign a non pointer value to a pointer).

>
> Is it sortable or not? If it is sortable, then & means "tail const". If it is not sortable, then how, in generic code, do we specify a sortable array of type T?

You are missing the point.  & does not mean tail const.

const(int)*[] a;

This is an array of int pointers that are TAIL CONST.  This does not mean * is tail const, does it?

& means 'a reference to', just like * means 'a pointer to'.

There is no ambiguity, no inconsistency, no violation of const rules.

>
> const(T)&[] a; // is it sortable or not?
>
> Now, if & is to mean "tail const", what do we do with:
>
> struct S
> { int i;
>   int* p;
>   void foo() { *p = 3; }
> }
>
> const(S)& s;
>
> Does that mean we can modify the members of S, but not what those members may point to? How do we specify that member function foo is "tail const" ?

Nope, it means you cannot modify members of S and you cannot call foo because it is not const.  const(S)& is a mutable reference to a const S just like const(S)* is a mutable pointer to a const S.

>
> And what do we do with:
>
> struct S { C c; }
> const(S)&[] a;
>
> Here, we've wrapped a class with a struct. Is it possible to make S behave like its underlying C? If not, we don't have general purpose UDTs. We have a heaping pile of special cases.

Why wouldn't you be able to?  The only overload that you cannot change is the assignment to another S reference.  You can overload assign to a C, because it is not a reference to an S.  In this case however, since S is const, you cannot assign to another C, just like you wouldn't be able to assign a C to a const(C).

You may look at this whole post and think "ok, & is just like *, why even have &?", well, the point is, for classes, & is a reference to the class data, not a reference to a class reference.  So for a class C, C& x is equivalent to C x.  The thing & gets us is a way to specify universally 'reference to'.  This would actually be very beneficial to generic programming.

There is one other possibility that you have not (at least publicly) considered, which I outlined in my post on 12/7 titled "The problem with const (and a solution)":

const(*C)*[] array;

This would be an array of mutable class references.  Having this possibility gives us at least a way to specify tail-const class references.  This solution has the benefit of not introducing references to other types (which would be a lot of work for you).  My preference is to have the reference operator, & if possible, but the *C solution is at least workable.

One further thing.  If you could at least pledge to have a way to specify a tail-const class reference before 2.0 is officially released, I would be willing to accept that for now.  I understand that adding something like the reference operator would be a lot of extra work besides the const stuff, so I would understand if you did your current const proposal with the intent of doing the reference work later.

-Steve


January 02, 2008
"Walter Bright" wrote
> Steven Schveighoffer wrote:
>> How about doing an in-place sort of an array of const classes?
>
> Good question, and one that I wrestled with a lot. The answer is, just like with strings, build another array.
>
>> const(C)[] myarray;
>>
>> ...
>> // swap two items i and j
>> auto tmp = myarray[i];
>> myarray[i] = myarray[j]; // error
>> myarray[j] = tmp; // error
>> ...
>>
>> Before you answer, building another array isn't acceptable, because that is a waste of resources and adds runtime.
>
> That's what our C intuition tells us, and that's why I resisted immutable strings for so long. But the way caching works on modern processors, our intuition about what is fast and slow about memory operations often is wrong. Furthermore, the bug resistance of immutable strings I believe more than makes up for it.

I think we are arguing different points here.  I am fine with const(T) meaning that T is const whether it be a class reference, or a struct, or primitive type.  I absolutely agree with that, and think it is required as you say.  My problem is that because classes are inherently references, you have to treat them that way, especially in regards to const.  If you can't split out the reference from the const-ness, then code is going to be extra awkward, and require workarounds that will at some point affect the complexity of algorithms.

I suppose my example is incorrectly stated, because with the current const system, you cannot state 'an array of mutable references to const classes'. So I agree that with an array of const class references:

const(C)[] myarray;

you should not be able to swap members.  But there should still be a way to specify an array of mutable references to const classes.  I don't care how it's done, but it cannot involve convoluted mechanisms to create backing arrays w/ pointers to references.  To have to allocate heap space to contain pointers when classes are already pointers seems inherently wasteful, and seems like I am fighting the language instead of having the language allow me to express what I want.

This is similar to not having constructors for structs.  If I want to create a const struct on the heap so I can store it somewhere outside the stack, instead of having a constructor I have to do:

S *s = new S;
s.value = 5;
const(S)*cs = s;

instead of:
const(S)*cs = new S(5);

Except in this case, the compiler can easily optimize the first to mean the second, and no bloat or runtime waste has occurred, because I'm just re-casting the pointer.  However for classes, I need:

C c = new C(5);
C *cp = new C*;
*cp = c;
const(C)* ccp = cp;

Now, I have an extra heap-allocated pointer for no good reason, and this is not optimized out by the compiler.

>> Tell me how you do this without having tail-const for class references. I couldn't care less about tail-const for structs, or tail-const for class instances (being defined as a state where the members of the class/struct are mutable, but any objects pointed to by its members are const).  This feature of being able to swap two references to const data is a very common algorithmic requirement.
>
> You're right, you cannot do it without tail-const for class references. But if you're desperate, you can make it work using casting.

Sure, but the behavior is undefined, right? :P


January 02, 2008
"Walter Bright" wrote
> Janice Caron wrote:
>> On 12/31/07, Steven Schveighoffer wrote:
>>> Except in the case of class references, right?  I find this to be the
>>> most
>>> unacceptable ommision.
>>
>> But when are you /ever/ going to need such a thing in a real-life use case, that you couldn't just code differently?
>
> Going through the thought process, I finally realized that a true reference type (which is what classes are) should look to the user like a value type, i.e. the reference part should not be separable from the value.

I'm not asking to have the value seperable from the reference.  I'm asking to have the ability to modify the constness of the value seperately from the constness of the reference.

>
>> I thought that ommission was bad at first too, until I thought it through. But Walter's right - its absence doesn't present any real programming problems. There's really nothing you can't just do another way. By contrast, its presence would really screw up the type system. Weighing up the pros and cons, I'd say its better to live with this than have to deal with all the complication that mutable-ref-to-const-heap-data would entail.
>
> It's interesting how having fully transitive const seems to affect the way you code and your notions about coding.

I have no problem with fully transitive const, and I think it's a much better improvement over C++.  I originally thought fully transitive const was an error, because there are rational reasons to want to have 'logical' const.  But in reality, logical const is just not const, and should be treated that way.

But the difference here is, having to port code due to transitive const does not add code bloat or affect runtime execution.  Having to port code due to not having tail-const class references does.

-Steve


January 02, 2008
On 1/2/08, Bill Baxter <dnewsgroup@billbaxter.com> wrote:
> Seems easy enough.  Just make it mean no-op for the things where it doesn't make sense, namely things that aren't pointers.
>
> struct Struct { int* p; int v; }
> class Class { int* p; int v; }
>
> const(int*)&     equiv to  const(int)*
> const(int)&      equiv to  const(int)
> const(Struct*)&  equiv to  const(Struct)*
> const(Struct)&   equiv to  const(Struct)
> const(Class)&    equiv to  const(Class)&

OK, at this point, arguments start to happen, because not everyone will agree on those definitions. For example, further down this thread, Stephen Schveighoffer argues that "If references are to be consistent, the meaning of an int reference is that it acts like an int until you assign to another int reference". Instantly, much confusion abounds because now people are arguing about what int& should mean, instead of what actually happens when you nail down a definition. But to avoid confusion here, we'll go with /your/ definition...


> So this doesn't seem to be the point yet where "things start to fall over" that you speak of.

Using your definitions, the compiler is unable to tell whether or not the following is OK:

    void f(T)(const(T)&[] a)
    {
        a[0] = a[1];
    }

This violates the D compiler rule of "context free grammar".

Of course you could define & differently, so that that particular problem does not arise - for example by inventing generic C++-like references to all types (which I believe is what Stephen suggests), but you will find then that /different/ problems arise.
January 02, 2008
On 1/2/08, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> "Walter Bright" wrote
> > Bill Baxter wrote:
> >> What was the fundamental problem with creating some syntax that would let you peel the reference part off a class reference?
> >
> > const(C)&[] a;
> >
> > is a sortable array of constant C's. But what is:
> >
> > const(int)&[] a;
>
> A sortable array of int references.

Now observe. Bill's idea of what & should mean is /different/ from Stephen's idea of what & should mean. Here, Walter responds to Bill's interpretation of what & should mean, and is immediately lambasted by Stephen who interprets it differently.

This puts poor Walter in a hopeless position. I really have sympathy for him.

I have come to the conclusion that /no/ syntax for references and/or tail constancy is devoid of its own insurmountable problems, but this is just very, very hard to get across because everyone is using different words for the same thing and/or the same word for different things.

We could probably have simplified the discussion a bit by saying: assume the syntax for mutable-ref-to-const data is "tailconst(T)", rather than "const(T)&", and then we wouldn't have got sidetracked into what "&" means in isolation.

But whatever the syntax, I must conclude that it can't be done, without violating one or more of the principles of D, and that's what Walter (and I) are trying to explain here.


> If references are to be consistent, the
> meaning of an int reference is that it acts like an int until you assign to
> another int reference.  If you assign it to another int reference, then type
> now references the same thing as the new int reference.  If you assign it to
> another int, that is a compiler error (can't assign a non reference value to
> a reference just like you can't assign a non pointer value to a pointer).

D doesn't have a reference-to-int type, only reference-to-class-data. This appears to be a new feature request.


> You may look at this whole post and think "ok, & is just like *, why even have &?", well, the point is, for classes, & is a reference to the class data, not a reference to a class reference.  So for a class C, C& x is equivalent to C x.  The thing & gets us is a way to specify universally 'reference to'.  This would actually be very beneficial to generic programming.

That leads to different problems. I know because that used to be my idea, until I got persuaded I was wrong.


> const(*C)*[] array;
>
> This would be an array of mutable class references.

It doesn't matter what the syntax is. We can pick amonst syntaxes till the cows come home. But whatever syntax you choose, you're still going to have to answer the question "what happens when C isn't a class?" (and cover all possible other types). And that's when problems start.


> One further thing.  If you could at least pledge to have a way to specify a tail-const class reference before 2.0 is officially released, I would be willing to accept that for now.

Walter cannot do that without sacrificing generic programming.
January 02, 2008
"Janice Caron" wrote
> On 1/2/08, Steven Schveighoffer wrote:
>> "Walter Bright" wrote
>> > Bill Baxter wrote:
>> >> What was the fundamental problem with creating some syntax that would
>> >> let
>> >> you peel the reference part off a class reference?
>> >
>> > const(C)&[] a;
>> >
>> > is a sortable array of constant C's. But what is:
>> >
>> > const(int)&[] a;
>>
>> A sortable array of int references.
>
> Now observe. Bill's idea of what & should mean is /different/ from Stephen's idea of what & should mean. Here, Walter responds to Bill's interpretation of what & should mean, and is immediately lambasted by Stephen who interprets it differently.

Bill's idea in this statement above was congruent with my idea.  In Bill's statement above, he states that there should be some syntax that allows you to peel the reference part off a class reference, which is encompassed by my idea as well.  Bill's response to Walter's statement in a different thread is not in congruence with mine, but as this is a new syntax, we are both allowed to interpret what we think it should be.  There is no previous definition here, Bill and I are not on a side together arguing the same thing.

>
> This puts poor Walter in a hopeless position. I really have sympathy for him.

Walter is a big boy.  I'm sure he can figure out how to sort through all the subtle differences in feature requests made on this forum.

>
> We could probably have simplified the discussion a bit by saying: assume the syntax for mutable-ref-to-const data is "tailconst(T)", rather than "const(T)&", and then we wouldn't have got sidetracked into what "&" means in isolation.
>
> But whatever the syntax, I must conclude that it can't be done, without violating one or more of the principles of D, and that's what Walter (and I) are trying to explain here.
>

I do not wish to have tailconst structures or tailconst builtin types (such as int).  What I want are consistent tailconst references.  The fact that I can specify a tailconst reference (pointer) to a struct or int, but not a tailconst reference to a class is a big hole in my opinion.

>
>> If references are to be consistent, the
>> meaning of an int reference is that it acts like an int until you assign
>> to
>> another int reference.  If you assign it to another int reference, then
>> type
>> now references the same thing as the new int reference.  If you assign it
>> to
>> another int, that is a compiler error (can't assign a non reference value
>> to
>> a reference just like you can't assign a non pointer value to a pointer).
>
> D doesn't have a reference-to-int type, only reference-to-class-data. This appears to be a new feature request.
>

Yes it is.  That was not a secret :)  Please view my post on 12/7 describing my original idea.

>
>> You may look at this whole post and think "ok, & is just like *, why even have &?", well, the point is, for classes, & is a reference to the class data, not a reference to a class reference.  So for a class C, C& x is equivalent to C x.  The thing & gets us is a way to specify universally 'reference to'.  This would actually be very beneficial to generic programming.
>
> That leads to different problems. I know because that used to be my idea, until I got persuaded I was wrong.

That anyone got persuaded that something is wrong is not proof that it is wrong.  "this leads to different problems" is not an explanation.

>
>
>> const(*C)*[] array;
>>
>> This would be an array of mutable class references.
>
> It doesn't matter what the syntax is. We can pick amonst syntaxes till the cows come home. But whatever syntax you choose, you're still going to have to answer the question "what happens when C isn't a class?" (and cover all possible other types). And that's when problems start.
>

If C isn't a reference type, then it doesn't compile.

struct S {};
class C {};
alias S *X;
alias int *Y;

const(*X)*[] array; // compiles, X is a reference type
const(*Y)*[] array; // compiles, Y is a reference type.
const(*C)*[] array; // compiles, C is a reference type.
const(*S)*[] array; // failure, S is a value type, cannot be dereferenced.
const(*int)*[] array; // failure, int is a value type, cannot be
dereferenced
const(*C)[] array; // failure, cannot declare a value type of *C.

Generic programming is already "broken" as you say because you can't treat a class as a value type.  There are differences which must be accounted for. For example:

class (T) C
{
   T x;
   this(int m)
   {
      // initialize T with m, assuming T is a class
      x = new T(m);
   }
}

What happens if T is a struct or int?  There is no generic way to do it.

>
>> One further thing.  If you could at least pledge to have a way to specify
>> a
>> tail-const class reference before 2.0 is officially released, I would be
>> willing to accept that for now.
>
> Walter cannot do that without sacrificing generic programming.

Allowing *C syntax would not break anything that is not already broken.  If you have an example of why it would break generic programming, please share with us.

And I invite Walter to still respond to this personally.

-Steve


January 02, 2008
On 1/2/08, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> That anyone got persuaded that something is wrong is not proof that it is wrong.

For clarification, what persuaded me was /logic/, not the force of Walter's personality! :-)

Unless I have misunderstood some step of the reasoning, every scheme to allow mutable-reference-to-const-class-data which has so far been proposed has been proven unworkable. The same logic should lead you, or anyone else, to the same conclusion.

Of course, this is not a proof that there is /no/ solution, only that the suggestions made up to this point all have fatal flaws.


> "this leads to different problems" is not an explanation.

Of course not. I'm hardly going to condense six months' worth of patient reasoning into a single sentence. You're proposing something which has already been proposed before (by me, as it happens), and has been shown not to work.


> If C isn't a reference type, then it doesn't compile.

That was my suggestion too. In the thread "const again", I said "I had in mind that that would be a syntax error", to which Walter replied "A special syntax for class types means that one has to know that type T is a class." And this is the flaw here: It must compile without knowing what T is.
January 02, 2008
"Janice Caron" wrote
> On 1/2/08, Steven Schveighoffer wrote:
>> That anyone got persuaded that something is wrong is not proof that it is wrong.
>
> Unless I have misunderstood some step of the reasoning, every scheme to allow mutable-reference-to-const-class-data which has so far been proposed has been proven unworkable. The same logic should lead you, or anyone else, to the same conclusion.
>
> Of course, this is not a proof that there is /no/ solution, only that the suggestions made up to this point all have fatal flaws.

I highly contest this statement.  I have never seen any statements that my proposal (dated 12/7) proved unworkable.  In fact, Walter completely ignored it.  The assumption you are making here is that because Walter didn't consider a possibility proves it's unworkable.

>> "this leads to different problems" is not an explanation.
>
> Of course not. I'm hardly going to condense six months' worth of patient reasoning into a single sentence. You're proposing something which has already been proposed before (by me, as it happens), and has been shown not to work.

I've been through that also :)  I read through all the 'const sucks' and 'const again' and all those proposals.  I have not yet seen proof that all solutions are unworkable.  It seems like (seems like, not is) Walter has put considerable thought and effort into trying to get tail const for class references to work, and could not form a solution that was reasonable. Therefore, he concluded it was not possible, and for some reason, will not even consider new ideas.

>> If C isn't a reference type, then it doesn't compile.
>
> That was my suggestion too. In the thread "const again", I said "I had in mind that that would be a syntax error", to which Walter replied "A special syntax for class types means that one has to know that type T is a class." And this is the flaw here: It must compile without knowing what T is.

Again, this inconsistency already exists today!  The inconsistency stems from the fact that classes ARE reference types.  There is no way around having to know that.

If you do:

setValueTo5(T)(T x)
{
   x.value = 5;
}

If T is a class, then it affects data outside the function.  If T is a struct, it does not.  We have to know if T is a class or not.

My counter-argument is "yes, it's inconsistent, but not any different than what we have today, how is this any worse?"

In fact, I believe the only way to make it consistent would be to make classes value types (which I believe is a bad idea).

-Steve