www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Why is separating class ind struct is bad?

reply Suliman <evermind live.ru> writes:
Here on forum I have found very interesting mention from 
Alexandrescu that:
"Rust has two advantages I can think of: it doesn't have the 
struct/class split that D does". Could anybody explain why it's 
bad, and maybe till Volta in WIP it's not later to change it.

Could anybody explain what this mean and why this way is not 
modern?
Mar 25 2016
next sibling parent Suliman <evermind live.ru> writes:
Sorry about Volta mention, I just make copy-past from my question 
there.
Mar 25 2016
prev sibling next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Saturday, March 26, 2016 06:26:44 Suliman via Digitalmars-d wrote:
 Here on forum I have found very interesting mention from
 Alexandrescu that:
 "Rust has two advantages I can think of: it doesn't have the
 struct/class split that D does". Could anybody explain why it's
 bad, and maybe till Volta in WIP it's not later to change it.

 Could anybody explain what this mean and why this way is not
 modern?
I expect that you're referring to a recent thread where interfacing with C++ was being discussed and how D compares to Rust in that regard. And when talking specifically about interfacing with C++, D's separation of structs and classes is potentially a problem, because that's not what C++ does. So, we're either limited in some respects with how D interfaces with C++ and/or the inferfacing code becomes more complicated. So, while the separation between structs and classes definitely good for D code in general, it does pose a problem when interfacing with C++. - Jonathan M Davis
Mar 26 2016
parent reply Dicebot <public dicebot.lv> writes:
On Saturday, 26 March 2016 at 07:42:43 UTC, Jonathan M Davis 
wrote:
 So, while the separation between structs and classes definitely 
 good for D code in general
I disagree with this statement. The very presence of utils like `Rebindable` or `scoped` indicates this wasn't a very clean design choice, not in a way it was implemented at least.
Mar 26 2016
next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Saturday, March 26, 2016 08:03:21 Dicebot via Digitalmars-d wrote:
 On Saturday, 26 March 2016 at 07:42:43 UTC, Jonathan M Davis

 wrote:
 So, while the separation between structs and classes definitely
 good for D code in general
I disagree with this statement. The very presence of utils like `Rebindable` or `scoped` indicates this wasn't a very clean design choice, not in a way it was implemented at least.
Rebindable is definitely a sign that there's a problem with how it was implemented, but it's also something that we've been able to work around. And while scoped may make sense in rare cases, pretty much by definition, if you're putting an object on the heap, polymorphism is not involved, and therefore it's completely unnecessary to use a class. Really, it's just an attempt to reuse code that's in a class in a context where polymorphism is unnecessary. So, arguably, needing scoped is a sign of bad design in the code that's using it. Certainly, if it's needed frequently, then the types being used with it really should have been structs. I won't disagree that the separation of classes and structs is not without its downsides, and the need for Rebindable does show that they way we implemented it wasn't as elegant as it arguably should have been, but overall, I think that the separation is a definite improvement over C++'s approach. Unless you use scoped (which is system), object slicing is a non-issue thanks to that separation, and since an object that needs polymorphism really doesn't make sense on the stack anyway, it separates user-defined types from the inheritance mess in the cases where it's unnecessary while still allowing inheritance to be used in a safe manner when it makes sense. - Jonathan M Davis
Mar 26 2016
parent Dicebot <public dicebot.lv> writes:
On Saturday, 26 March 2016 at 08:31:41 UTC, Jonathan M Davis 
wrote:
  And while scoped may make sense in rare cases, pretty much by 
 definition, if you're putting an object on the heap, 
 polymorphism is not involved, and therefore it's completely 
 unnecessary to use a class.
That I disagree with too. Putting class on stack simply means that current scope will outlive the usage of class instance - it has nothing to do with polymorphism. It is one of many memory optimizations. To explain a bit more, what I would consider convenient is to limit struct/class distinction to polymorphism exclusively, with not extra implications. So that you can still can do `MyClass on_stack;` (any `MyClass` is treated as type of object) but passing it to function wouldn't compile unless `ref` is also used.
Mar 26 2016
prev sibling next sibling parent Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 26 Mar 2016 9:05 am, "Dicebot via Digitalmars-d" <
digitalmars-d puremagic.com> wrote:
 On Saturday, 26 March 2016 at 07:42:43 UTC, Jonathan M Davis wrote:
 So, while the separation between structs and classes definitely good for
D code in general
 I disagree with this statement. The very presence of utils like
`Rebindable` or `scoped` indicates this wasn't a very clean design choice, not in a way it was implemented at least. Isn't that more a slam at the "deprecations" that still have no compiler enforcement 6 years down the line, and not directly related to class storage itself? I don't think Rebindable is a good example to show what is wrong with classes. More in the direction of head(const).
Mar 26 2016
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/26/2016 1:03 AM, Dicebot wrote:
 On Saturday, 26 March 2016 at 07:42:43 UTC, Jonathan M Davis wrote:
 So, while the separation between structs and classes definitely good for D
 code in general
I disagree with this statement. The very presence of utils like `Rebindable` or `scoped` indicates this wasn't a very clean design choice, not in a way it was implemented at least.
I've worked a lot with C++ aggregates that were confused about whether they were a floor wax or a dessert topping, and talking to their implementors showed they had little idea of the consequences (and bugs) of those choices. D's design has debatable flaws, but I'm confident they are much diminished over the flaws and traps in the C++ design.
 what I would consider convenient is to limit struct/class distinction to 
polymorphism exclusively, with not extra implications. So that you can still can do `MyClass on_stack;` (any `MyClass` is treated as type of object) but passing it to function wouldn't compile unless `ref` is also used. So where it is allocated you'd have 'MyClass' and everywhere it is used you'd have 'ref MyClass'. There are a lot of consequences of this, such as: 1. Want to switch between a class and a struct? You've got lots of editing to do. 2. You'd have to invent a way to do 'alias ref MyClass T;' 3. ref has special semantics in D about being a non-escaping pointer. This means issues if you want a 'ref MyClass' field in a struct. I'm not saying your idea is necessarily bad, but there is a heckuva lot to think about in interactions with other features. It's not so easy to produce a design that doesn't have a nail sticking up here or there. I don't know of any language that succeeded in that.
Mar 26 2016
parent reply Dicebot <public dicebot.lv> writes:
On 03/26/2016 02:01 PM, Walter Bright wrote:
 On 3/26/2016 1:03 AM, Dicebot wrote:
 On Saturday, 26 March 2016 at 07:42:43 UTC, Jonathan M Davis wrote:
 So, while the separation between structs and classes definitely good
 for D
 code in general
I disagree with this statement. The very presence of utils like `Rebindable` or `scoped` indicates this wasn't a very clean design choice, not in a way it was implemented at least.
I've worked a lot with C++ aggregates that were confused about whether they were a floor wax or a dessert topping, and talking to their implementors showed they had little idea of the consequences (and bugs) of those choices. D's design has debatable flaws, but I'm confident they are much diminished over the flaws and traps in the C++ design.
It is hard to argue that but I have never encountered any issues from actual confusion between struct and class in C++ (because there isn't any real difference), only from misusage of virtual types.
 what I would consider convenient is to limit struct/class distinction
to polymorphism exclusively, with not extra implications. So that you can still can do `MyClass on_stack;` (any `MyClass` is treated as type of object) but passing it to function wouldn't compile unless `ref` is also used. So where it is allocated you'd have 'MyClass' and everywhere it is used you'd have 'ref MyClass'. There are a lot of consequences of this, such as: 1. Want to switch between a class and a struct? You've got lots of editing to do.
I'd prefer to do lot of editing as opposed to lot of reading through to not introduce silent semantical bugs - which is exactly how switching between struct and class looks now. This is a major benefit, not an issue.
 2. You'd have to invent a way to do 'alias ref MyClass T;'
No way. `typedef int* p_int_t` is one of worst popular C idioms in my opinion. Typing extra three letters is a very minor price for having a code which looks like what it does.
 3. ref has special semantics in D about being a non-escaping pointer.
 This means issues if you want a 'ref MyClass' field in a struct.
MyClass* should also be applicable, I have only mentioned `ref` because non-escpaing is usually desired :) Btw implementation of DIP25 is heavily incomplete, I have never managed to make it work in any of practical attempts. There are quite some bug reports open : https://issues.dlang.org/buglist.cgi?quicksearch=dip25&list_id=207387
 I'm not saying your idea is necessarily bad, but there is a heckuva lot
 to think about in interactions with other features. It's not so easy to
 produce a design that doesn't have a nail sticking up here or there. I
 don't know of any language that succeeded in that.
Well, languages that simply declare everything a reference (JS) or dropped support for inheritance based polymorphism (Go, Rust) are much more consistent in that regard in my opinion :) There isn't much point in discussin details at this point as the ship has long sailed. But I definitely consider this one of underdesigned parts of D language.
Mar 28 2016
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/28/2016 9:56 PM, Dicebot wrote:
 It is hard to argue that but I have never encountered any issues from
 actual confusion between struct and class in C++ (because there isn't
 any real difference), only from misusage of virtual types.
Misusage of virtual types is the confusion.
 1. Want to switch between a class and a struct? You've got lots of
 editing to do.
I'd prefer to do lot of editing as opposed to lot of reading through to not introduce silent semantical bugs - which is exactly how switching between struct and class looks now. This is a major benefit, not an issue.
My experience is the opposite. I've found it quick & easy to attempt different data structures in D because of the minimal editing. In C++, I (and I suspect most people) just go with their first design because it is too much of a pain to edit all the usages. That is what I've dubbed "brittleness". One way I was able to quickly achieve speed in Warp was it was easy to try out different data structures. Much, much easier than in C++. The end result could be done in C++, but getting there would probably not have happened.
 2. You'd have to invent a way to do 'alias ref MyClass T;'
No way. `typedef int* p_int_t` is one of worst popular C idioms in my opinion. Typing extra three letters is a very minor price for having a code which looks like what it does.
You'd have to give up on writing generic code, then.
 Well, languages that simply declare everything a reference (JS) or
 dropped support for inheritance based polymorphism (Go, Rust) are much
 more consistent in that regard in my opinion :)
JS and Go throw a lot of capability under the bus in order to achieve consistency. It's a high price. I don't know enough about Rust to say what their tradeoffs here really are.
 There isn't much point in discussin details at this point as the ship
 has long sailed. But I definitely consider this one of underdesigned
 parts of D language.
True dat.
Mar 29 2016
prev sibling parent Danni Coy via Digitalmars-d <digitalmars-d puremagic.com> writes:
 Could anybody explain what this mean and why this way is not modern?
This is in the context of users migrating from C++, and it is bad in so far as it is not what a C++ programmer who hasn't read all the documentation would expect. The easiest path of migration would be if the programmer can design a program the same way they would a C++ program but using D syntax. this, transitive const and I think thread local by default make migration in this way harder. (Though probably worth it in the long run)
Mar 29 2016