August 15, 2007
Russell Lewis wrote:
> I don't see any fundamental reason why classes need to
> be reference types, other than history.

What about this situation.

//begin C++

class A{
        int val;
};
class B:public A{
        int foo;
};

int main(int argc,char**argvs){
        A a;
        B b;
        a=b;//HERE what happens to b's member foo?
}

//end C++

it's my impression that D's classes are reference types to avoid that specific problem.
August 15, 2007
Bill Baxter wrote:
> I'm starting to seriously wonder if it was a good idea to hide the pointers to classes.  It seemed kinda neat when I was first using D that I could avoid typing *s all the time.  But as time wears on it's starting to seem more like a liability.

Funny.  I started out having concerns about the inconsistency here and have decided it's a good thing :-)  I think the salient point is that classes in D do not and will never (as far as I know) have a value type--they are always a reference type, even when "scope" is used. However, requiring the user to supply an asterisk for all class variable declarations suggests to me that I can remove the asterisk and get a value type, which I cannot.  Back before the addition of "scope" I'd thought the syntax of scoped objects would be more like a value type:

MyClass c = MyClass();

in which case I think the presence of an asterisk may have been more justified, though the lack of pass-by-value would likely still have rendered it too confusing to be practical IMO.

I think in D we just have to accept that classes are special and deserve special syntax.

> In large part it all stems from the decision to make classes and structs different.  That seems nice in theory, but as time goes on the two are becoming more and more alike.

Personally, I think structs and classes are nothing alike in D and I consider this a good thing.  One of my pet peeves about C++ is that it has two keywords to represent the same darn thing.

The distinction between structs and classes really has more to do with how they are intended to be used than how they look.  Structs are simply simple aggregate data types that are allowed to contain functions for convenience.  In fact, I think the need for a copy ctor is debatable, given the purpose of structs (even though I've wanted one in the past), as is the need for a dtor.  Real ctor support seems entirely reasonable, however, because everything needs to be initialized, and ctors make this both convenient and efficient (the static opCall approach is a confusing hack).


Sean
August 15, 2007
Bill Baxter wrote:
> I'm starting to seriously wonder if it was a good idea to hide the pointers to classes.  It seemed kinda neat when I was first using D that I could avoid typing *s all the time.  But as time wears on it's starting to seem more like a liability.

Having classes be by reference only has some serious advantages:

1) Classes are polymorphic and inheritable. By making them reference only, the "slicing" problem inherent in C++ is completely and cleanly avoided.

2) You can't write a class in C++ without paying attention to assignment overloading and copy constructors. With classes as a reference type, these issues are simply irrelevant.

3) Value types just don't work for polymorphic behavior. They must be by reference. There's no way in C++ to ensure that your class instances are used properly by reference only (hence (2)). In fact, in C++, it's *extra work* to use them properly.

Value types are fundamentally different from reference types. D gives you the choice.
August 15, 2007
Bill Baxter wrote:
> Each of those things has different uses in C++.

These different uses should be decided by the designer of the class, not the user of the class.
August 15, 2007
Johan Granberg wrote:
> Russell Lewis wrote:
>> I don't see any fundamental reason why classes need to
>> be reference types, other than history.
> 
> What about this situation.
> 
> //begin C++
> 
> class A{
>         int val;
> };
> class B:public A{
>         int foo;
> };
> 
> int main(int argc,char**argvs){
>         A a;
>         B b;
>         a=b;//HERE what happens to b's member foo?
> }
> 
> //end C++
> 
> it's my impression that D's classes are reference types to avoid that
> specific problem.

That's known as the 'slicing' problem. It's pernicious in that it can be extremely hard to expose via testing or code reviews, yet will expose the program to unpredictable behavior.

While it is fairly obvious in your example that it is wrong, it can occur in cases that are *impossible* for the compiler to detect:

	void foo(A* a)
	{
		A ax = *a;
	}

	void main()
	{
		B b;
		foo(&b);
	}

This kind of error is impossible in D.
August 15, 2007
Gregor Richards wrote:
> OK, OK, I guess I should respond with an argument from computer science as well :)
> 
> In the normal definition of Object Orientation, an object is a means of storing a context in which operations can be performed. The abstraction behind this (yay we're modeling the universe in code but realistically we aren't yay) is irrelevant, as fundamentally OO is just a means of storing and passing contexts. Because this is a context, it makes no sense whatsoever to pass it around with duplication - duplicating contexts is nonsense.
> 
> structs are sort of a hack for compatibility and/or optimization. They are not contexts, they are means of creating more complicated values. While a "Point" could be a struct, really being a more complicated value, an "NPC" would always be a class, since it is a context.
> 
> The fact that this is inconsistent with C++ is irrelevant: D is more to the spirit of good OO.

You put your finger on the very good reason why polymorphic, inheritable types in D are restricted to being reference types, not value types. OOP requires this characteristic.

In C++, an OOP class can be used/misused by the user as a value type or a reference type, all out of the purview of the class designer. The class designer must control this, not the class user.
August 15, 2007
Bill Baxter wrote:
> Of course it comes at the expense of making it impossible to pass a class by value.

That is a deliberate positive feature, not an expense.
August 16, 2007
On Wed, 15 Aug 2007, Walter Bright wrote:

> Bill Baxter wrote:
> > I'm starting to seriously wonder if it was a good idea to hide the pointers to classes.  It seemed kinda neat when I was first using D that I could avoid typing *s all the time.  But as time wears on it's starting to seem more like a liability.
> 
> Having classes be by reference only has some serious advantages:
> 
> 1) Classes are polymorphic and inheritable. By making them reference only, the "slicing" problem inherent in C++ is completely and cleanly avoided.
> 
> 2) You can't write a class in C++ without paying attention to assignment overloading and copy constructors. With classes as a reference type, these issues are simply irrelevant.

They're _less_ relevant.  There's still the valid usecase of acting like a value to avoid instance sharing where copy construction and assignment's are.  This is where it's the author of the class making that decision rather than the user that you talk about in other responses to this thread.

That's the distinction between 'by value' vs 'by reference' and 'as a value' vs 'as references'.  IE, access vs behavior.  I like a strong enforcement and distinction between the access part, but I do believe that it should be possible for a class write to achieve value semantics.

> 3) Value types just don't work for polymorphic behavior. They must be by
> reference. There's no way in C++ to ensure that your class instances are used
> properly by reference only (hence (2)). In fact, in C++, it's *extra work* to
> use them properly.
> 
> Value types are fundamentally different from reference types. D gives you the choice.


August 16, 2007
Brad Roberts wrote:
> On Wed, 15 Aug 2007, Walter Bright wrote:
> 
>> Bill Baxter wrote:
>>> I'm starting to seriously wonder if it was a good idea to hide the pointers
>>> to classes.  It seemed kinda neat when I was first using D that I could
>>> avoid typing *s all the time.  But as time wears on it's starting to seem
>>> more like a liability.
>> Having classes be by reference only has some serious advantages:
>>
>> 1) Classes are polymorphic and inheritable. By making them reference only, the
>> "slicing" problem inherent in C++ is completely and cleanly avoided.
>>
>> 2) You can't write a class in C++ without paying attention to assignment
>> overloading and copy constructors. With classes as a reference type, these
>> issues are simply irrelevant.
> 
> They're _less_ relevant.  There's still the valid usecase of acting like a value to avoid instance sharing where copy construction and assignment's are.  This is where it's the author of the class making that decision rather than the user that you talk about in other responses to this thread.

I strongly feel that for that usecase, one should be using a value type, i.e. a struct, not a class. C++ classes are neither one nor the other, thereby doing neither well.


> That's the distinction between 'by value' vs 'by reference' and 'as a value' vs 'as references'.  IE, access vs behavior.  I like a strong enforcement and distinction between the access part, but I do believe that it should be possible for a class write to achieve value semantics.

I don't agree, I think there is much to be gained by drawing a strong distinction. Value and reference types are *fundamentally* different. Classes are an OOP type, and giving them value semantics introduces all the gotchas C++ has with them (like the slicing problem).

However, it is possible to use a struct to wrap a class reference.


>> 3) Value types just don't work for polymorphic behavior. They must be by
>> reference. There's no way in C++ to ensure that your class instances are used
>> properly by reference only (hence (2)). In fact, in C++, it's *extra work* to
>> use them properly.
>>
>> Value types are fundamentally different from reference types. D gives you the
>> choice.
> 
> 
August 16, 2007
Walter Bright wrote:
> Bill Baxter wrote:
>> Of course it comes at the expense of making it impossible to pass a class by value.
> 
> That is a deliberate positive feature, not an expense.

Ok.  Thanks for all the responses.

About slicing: it may be totally evil and impossible to debug when it occurs, but I have to say I can't recall ever being bitten by it in 10 years of C++ programming.  But I tend not to go crazy with the polymorphism in the first place, and if I do I guess I've been trained to avoid passing by-value classes around.  Just thinking about it triggers my "danger" reflex.  Always makes me queasy when I see things like std::string being passed around by value even though I know that most implementations are optimized to avoid copying the payload.  And I certainly would never try to assign different by-value classes to one another.

So yeh, you've eliminated slicing, but I'm not really convinced it was such a huge problem that it warranted a syntax upheaval in the first place.

But I do understand the logic that if you want polymorphic behavior you _have_ to call via a reference or pointer.  And if you *always* have to call via reference/pointer, then you might as well make that the default.

I think when it comes down to it the only things I'm really going to have left to complain about are missing features in structs:
1) the lack of constructors for structs (going to be fixed),
2) lack of (non-polymorphic) inheritance for structs (not so major),
3) lack of const reference for convenient passing of structs around (from what I understand the current situation in D 2.0 is that the closest you can come is a const pointer to a struct, meaning that every time I call the function I have to remember to dereference the args: rotate(&quat,&vec).  Bleh.)


Initially I unrealistically thought D's class/struct thing was going to be some kind of super magic bullet, recently it dawned on me that it wasn't panning out that way (hence these posts), and finally I guess I'm coming to the conclusion (thanks for all the input!) that D's class/struct thing is not worse than C++, but isn't necessarily better than C++'s way, either.  They both have issues, just the issues are shifted around.   And maybe I'll eventually come to view D's way as slightly better -- esp. if the 3 issues above are sorted out ;-).  But I've been permanently disabused of my illusions that separating class/struct is some sort of magic cure-all.  :-)

--bb