Re: Why are structs and classes so different?

2022-05-19 Thread Ola Fosheim Grøstad via Digitalmars-d-learn

On Wednesday, 18 May 2022 at 10:53:03 UTC, forkit wrote:

Here is a very interesting article that researches this subject.


Yeah, he got it right. It is syntax sugar that makes verbose C++ 
code easier to read. Use struct for internal objects and tuple 
like usage and class for major objects in your model.


But Simula used class for more: coroutines and library modules. 
So, you could take a class and use its scope in your code and 
thereby get access to the symbols/definitions in it.


The successor to Simula, called Beta, took it one step further 
and used the class concept for functions, block scopes, loops, 
almost everything.


The language Self went even further and collapsed the concept of 
class and object into one... perhaps too far... People prefer 
Typescript over Javascript for a reason. 


Re: Why are structs and classes so different?

2022-05-18 Thread forkit via Digitalmars-d-learn

On Sunday, 15 May 2022 at 21:33:24 UTC, Ali Çehreli wrote:


I still think my answer is the real one. My implied question 
remains: Why does C++ have struct and class disticnction? I 
know they have different default access specifications but does 
that warrant two kinds?




Here is a very interesting article that researches this subject.

https://belaycpp.com/2021/09/17/history-of-c-explanation-on-why-the-keyword-class-has-no-more-reason-to-exist/



Re: Why are structs and classes so different?

2022-05-17 Thread Johan via Digitalmars-d-learn

On Monday, 16 May 2022 at 21:20:43 UTC, Ali Çehreli wrote:

On 5/16/22 10:35, Johan wrote:

> What is very problematic is that you cannot see the
difference in
> syntax. In my opinion it would have been much better if the
language
> required using a `*` for class types: for example `Foo* a`,
and `Foo a`
> would simply give a compile error.

I see. Is it really a high mental load without the syntax? I 
seems to just work in my programs but perhaps because I am the 
main programmer and classes are very rare anyway.


Also, same syntax is said to help with template code but 
perhaps the argument there is a template must be written either 
for value types or reference types? I am not sure.


`Foo a = b;`
What does that do?
A benefit of statically typed languages is that you know what 
simple code will do. But for this simple statement, you actually 
don't know. Perhaps it is a copy, perhaps not.
I understand that also structs can have reference-like semantics 
with copies if they contain pointers, but it is strange that the 
language at a fundamental basic level has this ambiguity of user 
types.
Indeed the same-syntax template argument is bogus, for exactly 
this reason that you don't know what the code is doing. If the 
language deems it important enough to separate value types from 
reference types, then why does it allow passing _both_ to 
`foo(T)(T t)` ?


-Johan








Re: Why are structs and classes so different?

2022-05-17 Thread Ali Çehreli via Digitalmars-d-learn

On 5/17/22 07:40, Kevin Bailey wrote:

> Foo foo;
>
> is undefined behavior waiting to happen, that I can't detect at a glance.

Foo is null there. (I don't remember whether accessing through null 
reference is undefined or segmentation fault (on common systems).) Using 
foo will not do some random thing.


> - A *secondary* goal would be for class objects to be able to have
> deterministic destructors like structs.

I understand. D uses garbage collection by default for classes.

Having this choice of struct versus class is useful. This has been 
mentioned before: structs are the predominant user-defined type in D. 
Classes are only for polymorphic behavior.


>> C++ does not use terms "value type" and "reference type" to make any
>> distinction but the rules above are proof enough for me that C++
>> implicitly divides user-defined types into such categories.
>
> I would beg to differ here. In C++, all types are value types, until you
> add punctuation. This is one of the things that I like about C++, that I
> trip over in other languages. e.g. In Rust, there are 2 similar types,
> is it int and float, where one is copyable and the other move-only? How
> can you write generic code in that environment?
>
> Yes, in C++, you have to worry about slicing and copying singletons, but
> these are problems in front of you. It's the problems that sneak up
> behind you that I worry about.

Yes, that's where we differ. :) You see problems that can sneak up, I 
see problems disappeared.


>> Ok, I think I see better now. You would like members of a class
>> recursively placed next to each other in memory. What if a polymorphic
>> type had a polymorphic member?
>
> You mean like a string? I don't have a problem with this:
>
> class MyString {
> uint length;
> ...pointer to data...
> }
>
> void func() {
> MyString s;

It is easy to adapt to the following syntax:

  auto s = /* some expression */;

  auto s = new MyString();
  auto s = scoped!MyString();
  scope s = new MyString();

MyString could have been a member variable, which would be initialized 
in a constructor (or not; everything gets the .init value).


> if (s.length == 0)  // I want this to be perfectly safe.
>writeln("empty");
> // 's' destroyed here, could do something useful

Such types are rare in my experience especially because of the GC. Once 
you remove explicit freeing of memory, most destructors disappear as 
well. In cases where deterministic destruction is needed, we have a 
number of options all of which were used by me:


- scope has already been mentioned.

- RAII: MyString could be owned by a struct (e.g. MyStringCloser) either 
for every use or wherever it makes sense.


- scope (exit) { s.close(); }

> }
>
> In D, some objects can do this; some can't!

Perhaps in a template, yes. Then we may have to do some introspection there:

  static if (is (T == class)) {
auto s = new T();
  } else {
auto S = T();
  }

I've seen similar concerns raised about this difference before.

Just checked: Yes, there are 45 occurrences of that check under 
/usr/include/dlang/dmd on my system, which includes Phobos and core.


> Python is a toy language, right? I'm not aware of any large projects in
> it. (The largest I worked with was 138 files, 31k lines - tiny.)

Wow! That's way beyond my pay grade. :) But I have a feeling you will 
like D despite its differences.


> Perhaps the project is small.

Correct. I would be happy if others chimed in but this struct/class 
difference is not a common issue with D at all.


> Perhaps your IDE colors classes
> brightly.

Yes, I use syntax highlighting with muted colors. :) I still use Emacs 
with a suboptimal configuration. (I need to work on fixing that already. :/)


Ali



Re: Why are structs and classes so different?

2022-05-17 Thread Adam D Ruppe via Digitalmars-d-learn

On Tuesday, 17 May 2022 at 14:40:48 UTC, Kevin Bailey wrote:

Foo foo;

is undefined behavior waiting to happen, that I can't detect at 
a glance.


It is actually perfectly well defined - for the class, it will be 
null, and this will kill the program if you use it.


You might not like that definition, but that's how it is defined.

- A *secondary* goal would be for class objects to be able to 
have deterministic destructors like structs.


they can with the `scope` keyword.


The first looks trivial: Just have it allocate a Foo by default.


Worth noting that D *never* calls a user defined function on 
variable declaration; there are no default constructors.


Even if it is a struct, it never actually calls a constructor, it 
just copies over the default init value.


This is different than classes, which have some kind of 
constructor call any time you make one.



I think it is probably this default constructor stance that the 
rest flows from: a class is assumed to encapsulate some 
non-trivial state so it has a constructor, interfaces, object 
identities, etc. A struct is more of a plain collection of data.


Of course, in practice the lines are more blurred than that, but 
I think that's where it comes from. Especially if you look at the 
older D versions when structs didn't support constructors at all.


Re: Why are structs and classes so different?

2022-05-17 Thread Kevin Bailey via Digitalmars-d-learn

Hi again Ali!

On Monday, 16 May 2022 at 21:58:09 UTC, Ali Çehreli wrote:


I for one misunderstood you. I really thought you were arguing 
that struct and class should be the same.


To be specific:
- My *primary* concern is that:

Foo foo;

is undefined behavior waiting to happen, that I can't detect at a 
glance.


- A *secondary* goal would be for class objects to be able to 
have deterministic destructors like structs.


The first looks trivial: Just have it allocate a Foo by default.

The second looks like it could have been designed that way, 
albeit with other minor changes to the language, and, I was 
curious why it wasn't.


C++ is proof that it can indeed work the other way. However, 
for it to work correctly, programmers must follow guidelines. 
Here are four:


But again, from this discussion, it seems D could have simply had 
pass-by-reference for class objects, preserving everything D 
strives for, but by-value stack initialization, providing what 
people expect from stack objects. There's no need to drag C++ 
design into it.


C++ does not use terms "value type" and "reference type" to 
make any distinction but the rules above are proof enough for 
me that C++ implicitly divides user-defined types into such 
categories.


I would beg to differ here. In C++, all types are value types, 
until you add punctuation. This is one of the things that I like 
about C++, that I trip over in other languages. e.g. In Rust, 
there are 2 similar types, is it int and float, where one is 
copyable and the other move-only? How can you write generic code 
in that environment?


Yes, in C++, you have to worry about slicing and copying 
singletons, but these are problems in front of you. It's the 
problems that sneak up behind you that I worry about.


Ok, I think I see better now. You would like members of a class 
recursively placed next to each other in memory. What if a 
polymorphic type had a polymorphic member?


You mean like a string? I don't have a problem with this:

class MyString {
   uint length;
   ...pointer to data...
}

void func() {
   MyString s;
   if (s.length == 0)  // I want this to be perfectly safe.
  writeln("empty");
   // 's' destroyed here, could do something useful
}

In D, some objects can do this; some can't!

I don't understand that example. I see a programmer error of 
casting a Foo to a Bar.


Correct, I was responding to a comment. I was pointing out that 
the only "slicing" that we need to worry about with by-reference 
is if we are already doing something wrong, and that D won't help 
you there.


Did you mean C#? C# is like D: structs are value types and 
classes are reference types.


You missed the part where I said, "ignoring structs". :-)

With all due respect, based on other conversation here, may I 
assume you those projects were based on C++? If you also had 
any language with reference types like Python, did you have the 
similar issues with those languages?


Python is a toy language, right? I'm not aware of any large 
projects in it. (The largest I worked with was 138 files, 31k 
lines - tiny.)


Java would be a better comparison, but it has auto-closable 
objects, unlike D and Python. Perhaps it has succeeded because 
there are so few types that *aren't* by reference. Perhaps one 
just gets used to it. (I've written Android apps, but I would 
never write a long-running service in it.)


This is unfortunate for D where you have to keep track.

I accepted D's struct/class separation since the beginning and 
never had any issue with it. It just worked for me.


Perhaps you are familiar with the types that you work with on a 
daily basis. Perhaps the project is small. Perhaps your IDE 
colors classes brightly. I don't know, but an anecdote doesn't 
mean much compared to the fact that nearly all large projects are 
in C++, and I don't mean "due to inertia." I mean, "and they're 
successful."




Re: Why are structs and classes so different?

2022-05-16 Thread Ali Çehreli via Digitalmars-d-learn

On 5/15/22 21:20, Tejas wrote:

> Never say never :
>
> https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

Thanks! I am reading it now.

> Also, there's `opPostMove` already within the language

I see: It indeed appears on some pages on dlang.org but the language 
spec has no mention of it. :) It looks like a DbI (design by 
introspection) thing in Phobos or core.


It looks like structs with self references are allowed now as long as 
they do the right thing in their opPostMove. Cool...


Ali



Re: Why are structs and classes so different?

2022-05-16 Thread Ali Çehreli via Digitalmars-d-learn

On 5/16/22 08:18, Kevin Bailey wrote:

> I was asking, "Why is it this way? Why was it
> designed this way?

I for one misunderstood you. I really thought you were arguing that 
struct and class should be the same.


> What bad thing happens the other way?"

C++ is proof that it can indeed work the other way. However, for it to 
work correctly, programmers must follow guidelines. Here are four:



http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c67-a-polymorphic-class-should-suppress-public-copymove


http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c145-access-polymorphic-objects-through-pointers-and-references

  http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-slice


http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#e15-throw-by-value-catch-exceptions-from-a-hierarchy-by-reference

C++ does not use terms "value type" and "reference type" to make any 
distinction but the rules above are proof enough for me that C++ 
implicitly divides user-defined types into such categories.


D is this way because someone realized that there are different kinds of 
user-defined types.


> When I think
> about most things in D, I can at least think of a reason, even if I
> don't agree with it. But not putting class objects on the stack makes no
> sense to me (assuming we still pass by reference.)

Ok, I think I see better now. You would like members of a class 
recursively placed next to each other in memory. What if a polymorphic 
type had a polymorphic member?


class Student : Human {
  double a;
  Pet pet;
  double b;
}

Could Pet (which could be a Cat or a Dog, etc.) fit between a and b? 
Yes, emplacing the top-level object in our memory would have some value 
but not all members would be there. For example, D's dynamic arrays are 
like C++'s vector. What if the type had a vector? It's element would not 
be on the stack.


Granted, the type could have a specific Pet:

  Dog pet;

but I would argue that not all Students would have a Dog.

> Reasons below.
>
> Ola, your response is very interesting. I would say that assignment
> isn't any more or less of an issue, as long as you pass by reference:
>
> // Using C syntax to make intent clear.
> class Foo { int x; }
> class Bar: Foo { int y; }
>
> void func1(Bar* bar) {
>bar.y = 4; // boom
> }
>
> void func2(Bar* bar) {
>Foo* foo = bar;
>foo.x = 3; // this is fine
>
>foo = new Foo;
>func1(cast(Bar*)(foo)); // uh oh
> }
>
> Illuminating comment about the ABI.

I don't understand that example. I see a programmer error of casting a 
Foo to a Bar.


> re: pass-by-reference and performance, agreed, this is why we pass
> integers in registers. But a struct on the stack is just as fast to
> access locally through a pointer register - "[bp+6]" - as remotely, yes?

I thought more of this and conferred with a colleague who degisned parts 
of Intel CPUs. He agrees with you: Even if passed by reference, the 
objects are in CPU cache anyway and the pointer arithmetic is made in 
the core that all is good.


He went further to suggest yes, by-reference will be faster for large 
structs but there is no clear answer because it all depends on the 
speeds of the core, cache (including the size), memory.


> D went the Java path: You can have any color you
> like, as long as it's grey.

Did you mean C#? C# is like D: structs are value types and classes are 
reference types.


> I agree that C++'s organic growth allows programmers to do things they
> shouldn't, and requires that they be more careful. But I would not have
> gone from there to Java.

Not at all!

> I've learned from all the projects that I've been on
> that we need all these types.

With all due respect, based on other conversation here, may I assume you 
those projects were based on C++? If you also had any language with 
reference types like Python, did you have the similar issues with those 
languages?


I accepted D's struct/class separation since the beginning and never had 
any issue with it. It just worked for me.


Ali



Re: Why are structs and classes so different?

2022-05-16 Thread Ali Çehreli via Digitalmars-d-learn

On 5/16/22 13:48, Kevin Bailey wrote:

> a large code-base written
> by thousands of other people. I do every day. I can't make them name
> things in any special way.

I think we need a comparable D project to know whether this is really an 
issue.


> But when I see the above code, I need to know
> exactly what it does with just a scan.

Most IDEs and editors show whether a type is a class or a struct.

Ali



Re: Why are structs and classes so different?

2022-05-16 Thread Ali Çehreli via Digitalmars-d-learn

On 5/16/22 10:35, Johan wrote:

> What is very problematic is that you cannot see the difference in
> syntax. In my opinion it would have been much better if the language
> required using a `*` for class types: for example `Foo* a`, and `Foo a`
> would simply give a compile error.

I see. Is it really a high mental load without the syntax? I seems to 
just work in my programs but perhaps because I am the main programmer 
and classes are very rare anyway.


Also, same syntax is said to help with template code but perhaps the 
argument there is a template must be written either for value types or 
reference types? I am not sure.


> A few years ago when I taught C++, this was 50% of the reason for me not
> to teach D.

That's unfortunate. :(

> I see a big confirmation of that decision in this thread.

Luckily, in my experience such threads are not frequent. Still, we may 
see a related topic at DConf. ;)


Ali



Re: Why are structs and classes so different?

2022-05-16 Thread Kevin Bailey via Digitalmars-d-learn

On Monday, 16 May 2022 at 19:06:01 UTC, Alain De Vos wrote:

A new syntax like "*" should introduce something new.
If it's not needed for classes why introduce it.


Hi Alain!

I have to sympathize with Johan. If you see:

Foo foo = get_foo();
call_function(foo);

can 'foo' change in 'call_function()' ? Is it by-reference or is 
it by value?


Foo* foo = get_foo();

How about now? Pretty obvious.

call_function();

Also obvious.

To re-phrase your claim, a new syntax doesn't need to introduce 
something new. Syntax is there to convey information.



If you don't know if something is a class name it class_blabla.
Just remember the effect of "="


ah, I see the problem. You've never worked in a large code-base 
written by thousands of other people. I do every day. I can't 
make them name things in any special way. But when I see the 
above code, I need to know exactly what it does with just a scan.




Re: Why are structs and classes so different?

2022-05-16 Thread Alain De Vos via Digitalmars-d-learn

A new syntax like "*" should introduce something new.
If it's not needed for classes why introduce it.
If you don't know if something is a class name it class_blabla.
Just remember the effect of "="


Re: Why are structs and classes so different?

2022-05-16 Thread Johan via Digitalmars-d-learn

On Sunday, 15 May 2022 at 16:36:05 UTC, Ali Çehreli wrote:

On 5/15/22 08:26, Kevin Bailey wrote:

> structs and classes are so different.

I think a more fundamental question is why structs and classes 
both exist at all. If they could be the same, one kind would be 
sufficient. And the answer is there are value types and there 
are reference types in programming.


What is very problematic is that you cannot see the difference in 
syntax. In my opinion it would have been much better if the 
language required using a `*` for class types: for example `Foo* 
a`, and `Foo a` would simply give a compile error.
A few years ago when I taught C++, this was 50% of the reason for 
me not to teach D. I see a big confirmation of that decision in 
this thread.


-Johan



Re: Why are structs and classes so different?

2022-05-16 Thread H. S. Teoh via Digitalmars-d-learn
On Mon, May 16, 2022 at 05:02:57PM +, IGotD- via Digitalmars-d-learn wrote:
> On Sunday, 15 May 2022 at 16:08:01 UTC, Mike Parker wrote:
> > 
> > `scope` in a class variable declaration will cause it to the class
> > to be allocated on the stack.
> > 
> 
> Common practice is that a class has class members itself. So where are
> they allocated? Most likely is only the top class that is on the
> stack, the class members are allocated on the heap because the
> constructor is already compiled.
> 
> That scope isn't that useful unless you have it like C++, that expands
> class members in the parent class.
[...]

C++ embedded class members suffer from the same problems as by-value
class objects. I.e., object truncation when you assign a derived class
member to it.  The only way to avoid this problem in C++ is to turn them
into pointers, at which point it becomes equivalent to D class members
that by default are reference types.

In D, if you have members that you want to have by-value semantics, just
use structs instead.  In general, in my own D code I rarely use classes.
Structs are my go-to constructs; only when there is good reason I use
classes -- usually when I need inheritance, which is also when by-value
types would encounter truncation issues.  Since it *is* possible to pass
around pointers to structs, I don't really see much reason for using
classes if you don't need inheritance, i.e., when you'll never run into
truncation issues.

So IMO D's design of structs and classes makes much more sense than in
C++, where `struct` and `class` means essentially the same thing (just
with some different default protections -- just lip gloss, really), and
where the unclear intention of whether you want a by-value or
by-reference type means that truncation issues keep cropping up. D's
choice to settle this decision and bake it into the language IMO was the
right choice. (Now obviously, this implies that the usage of structs /
classes will differ between D and C++... but then again, that's why this
is D, not C++. I want to speak idiomatic D, not D with a C++ lisp. :-P)


T

-- 
The richest man is not he who has the most, but he who needs the least.


Re: Why are structs and classes so different?

2022-05-16 Thread IGotD- via Digitalmars-d-learn

On Sunday, 15 May 2022 at 16:08:01 UTC, Mike Parker wrote:


`scope` in a class variable declaration will cause it to the 
class to be allocated on the stack.




Common practice is that a class has class members itself. So 
where are they allocated? Most likely is only the top class that 
is on the stack, the class members are allocated on the heap 
because the constructor is already compiled.


That scope isn't that useful unless you have it like C++, that 
expands class members in the parent class.






Re: Why are structs and classes so different?

2022-05-16 Thread Ola Fosheim Grøstad via Digitalmars-d-learn

On Monday, 16 May 2022 at 15:18:11 UTC, Kevin Bailey wrote:
I would say that assignment isn't any more or less of an issue, 
as long as you pass by reference:


What I mean is if you write your code for the superclass, and 
later add a subclass with some invariants that depends on the 
superclass fields… then upcast an instance of the subclass to the 
superclass and pass it on… your risk the same issue. The subclass 
invariant can be broken because of sloppy modelling.


The premise for "slicing" being an issue is that people who write 
functions have no clue about how the type system works or how to 
properly model. So it is a lot of fuzz over nothing, because with 
that assumption you can make the exact same argument for 
references. (I've never had any practical issues related to 
"slicing", ever…)


Besides, slicing can very well be exactly what you want, e.g. if 
you have a super-class "EntityID" and want to build an array of 
all the EntityIDs… nothing wrong about slicing out the EntityID 
for all subclass instances when building that array.


Now, there are many other issues with C++, mostly related to the 
fact that they give very high priority to avoid overhead. E.g. 
take a new feature like std::span, if you create a subspan 
("slice" in D terminology) and the original span does not contain 
enough elements then C++ regards that as undefined behaviour and 
will happily return a span into arbitrary memory past the end of 
the original span. C++ is very unforgiving in comparison to 
"higher level" languages like D.


If we extend this reasoning to D classes, one can say that D 
classes are convenience constructs that does not pay as much 
attention to overhead. One example of this is how interfaces are 
implemented, each interface will take a full pointer in every 
instance of the class. The monitor mutex is another example. And 
how pointers to classes are different (simpler syntax) than 
pointers to struct also suggests that classes are designed more 
for convenience than principles.


Whether this is good or bad probably depends on the user group:

1. Those that are primarily interested in low level with a bit of 
high level might think it is "too much" and favour structs.


2. Those that are primarily interested in high level with a bit 
of low level might think otherwise.


In C++ everyone belong to group 1. In other system languages such 
as D and Rust you probably have a large silent majority in group 
2. (All those programmers that find C++ to be too brittle or hard 
to get into, but want comparable performance.)











Re: Why are structs and classes so different?

2022-05-16 Thread Kevin Bailey via Digitalmars-d-learn

Great responses, everyone. I'll try to address all of them.

Mike, I know the rules. I was asking, "Why is it this way? Why 
was it designed this way? What bad thing happens the other way?" 
When I think about most things in D, I can at least think of a 
reason, even if I don't agree with it. But not putting class 
objects on the stack makes no sense to me (assuming we still pass 
by reference.) Reasons below.


Ola, your response is very interesting. I would say that 
assignment isn't any more or less of an issue, as long as you 
pass by reference:


// Using C syntax to make intent clear.
class Foo { int x; }
class Bar: Foo { int y; }

void func1(Bar* bar) {
  bar.y = 4; // boom
}

void func2(Bar* bar) {
  Foo* foo = bar;
  foo.x = 3; // this is fine

  foo = new Foo;
  func1(cast(Bar*)(foo)); // uh oh
}

Illuminating comment about the ABI.

Ali, I've never liked the distinction between 'struct' and 
'class' in C++ either, but that's no reason to actually make them 
different. It's a reason to remove 'class' and save the keyword.


re: pass-by-reference and performance, agreed, this is why we 
pass integers in registers. But a struct on the stack is just as 
fast to access locally through a pointer register - "[bp+6]" - as 
remotely, yes?


Finally, 'scope' is nice but it doesn't solve the segfault issue.

HS Teoh: See above for my responses about assignment and 'scope'.

bauss: "But that's not entirely true as you can allocate a struct 
on the heap as well."


forkit: "where they live in memory should be less of the 
programmers concern, and more an implementation issue (although 
some programmers will of course consider this as well)."


Precisely. You can allocate structs, and you can put class 
objects in 'scope' variables. (I'm not sure if this was your 
intent, forkit, but) a class object can live on the stack just as 
easily as on the heap, as long as you pass by reference. The only 
danger is if a called function tries to own a stack allocated 
object, but this is a concern for heap allocated objects too. 
This is why C++ has moveable types and unique_ptr.


Walter, Thanks for the insightful reply! I'm getting the sense 
that the decision was made in order to make the language simpler. 
That is, ignoring struct's, D went the Java path: You can have 
any color you like, as long as it's grey.


I agree that C++'s organic growth allows programmers to do things 
they shouldn't, and requires that they be more careful. But I 
would not have gone from there to Java.


I think an interesting middle-ground would be a language that had 
"template" types - Copyable, MoveOnly, Interface, Singleton, 
FactoryBuilt, etc. I've learned from all the projects that I've 
been on that we need all these types. We can either ask 
programmers to hand-craft them, or we can provide them. Note that 
in C++, we can get pretty close:


class Foo: Moveable { ...

And then there's the segfault issue. I think that, if we're going 
to ignore a huge problem like that, there has to be very strong 
reasons. From this discussion, it doesn't sound like they were 
very strong.


Of course, it's done and there's little changing it. A seemingly 
harmless fix would be to not require 'new':


Foo foo; // this allocates an object
Foo foo = null; // this does not
Foo foo = function(); // this doesn't either

In fact, I suspect we could make that change today and few would 
notice. Nevertheless, I'm still a little shocked that this isn't 
existing behavior.


cheers all



Re: Why are structs and classes so different?

2022-05-16 Thread bauss via Digitalmars-d-learn

On Sunday, 15 May 2022 at 15:59:17 UTC, Alain De Vos wrote:

Can i summarize ,
structs are value-objects which live on the stack.
class instances are reference objects which live on the heap.


But that's not entirely true as you can allocate a struct on the 
heap as well.


The real difference is inheritance and polymorphism, not 
allocation and where the memory lives.


Re: Why are structs and classes so different?

2022-05-15 Thread Tejas via Digitalmars-d-learn

On Sunday, 15 May 2022 at 21:33:24 UTC, Ali Çehreli wrote:


D programmers don't write move constructors or move assignment. 
Such concepts don't even exist.




Never say never :

https://github.com/dlang/DIPs/blob/master/DIPs/DIP1040.md

Walter is one of the authors of the DIP

Also, there's `opPostMove` already within the language



Re: Why are structs and classes so different?

2022-05-15 Thread Walter Bright via Digitalmars-d-learn

On 5/15/2022 8:26 AM, Kevin Bailey wrote:

I'm trying to understand why it is this way.

Great question.

The difference, in a nutshell, is a struct is a value type, and a class is a 
reference type. This difference permeates every facet their behavior.


In C++, a struct can designed to be a value type or a reference type. But C++ 
does not recognize the difference, and so you can pass a reference type by 
value, which leads to all sorts of problems. You'll see C++ structs confused 
about what they are, as the designer didn't know the difference and would put a 
foot in both camps. A common example of this confusion is putting in virtual 
functions but neglecting to make the destructor virtual.


D draws a hard distinction between the two, making it both self-documenting, and 
heading off all sorts of errors from misusing one as the other.


A reference type is inherently a polymorphic type (i.e. virtual functions). 
Polymorphism via inheritance makes no sense for a value type.


Copy constructors make no sense for a polymorphic type, but are sensible for a 
value type.


And so on.

A strong distinction between value and reference types has turned out well for 
D. Naturally, some people still want a type to be both a floor wax and a dessert 
topping, but D is purposely going to make it difficult to do that.


P.S. Yes, you can pass a struct by reference with the `ref` keyword. That's not 
polymorphic behavior, though.


P.P.S. Yes, you can allocate a class instance on the stack rather than the GC by 
using the `scope` storage class. It will still be a reference type, but the 
compiler won't allow that reference to live longer than its stack frame. Java 
will automagically allocate classes on the stack if it can determine it cannot 
escape.


Re: Why are structs and classes so different?

2022-05-15 Thread forkit via Digitalmars-d-learn

On Sunday, 15 May 2022 at 15:26:40 UTC, Kevin Bailey wrote:
I've done some scripting in D over the years but I never dug 
into D until recently. I'm going through Learning D and I was 
reminded that structs and classes are so different.


- struct methods are non-virtual while class methods are virtual
- Thus, structs can't inherit, because how would you find the 
child's destructor given a parent pointer?

- On the stack, structs by-value but classes are by-reference

I'm trying to understand why it is this way. I assume that 
there's some benefit for designing it this way. I'm hoping that 
it's not simply accidental, historical or easier for the 
compiler writer.


A virtual function call has to pass through a virtual function 
look-up, and thus there is (some) overhead involved.


Thus, by design, structs avoid this overhead (completely).

It's also (I think) another reason why D does not support 
multiple inheritance. Since you would need multiple virtual 
function tables.


Re: Why are structs and classes so different?

2022-05-15 Thread forkit via Digitalmars-d-learn

On Sunday, 15 May 2022 at 15:59:17 UTC, Alain De Vos wrote:

Can i summarize ,
structs are value-objects which live on the stack.
class instances are reference objects which live on the heap.


the real difference, is that structs, being value types, are 
passed by value, and classes, being reference types, are passed 
by reference.


this is the most important difference to be aware of.

where they live in memory should be less of the programmers 
concern, and more an implementation issue (although some 
programmers will of course consider this as well).


btw. where does a struct, inside a class live?



Re: Why are structs and classes so different?

2022-05-15 Thread H. S. Teoh via Digitalmars-d-learn
On Sun, May 15, 2022 at 08:05:05PM +, Kevin Bailey via Digitalmars-d-learn 
wrote:
[...]
> But I asked a different question: Why can't I put a class object on
> the stack? What's the danger?
[...]

You can. Use core.lifetime.emplace.

Though even there, there's the theoretical problem of stack corruption:
if you have an emplaced class object O and you try to assign a derived
class object to it, you could end up trashing your stack (the derived
object doesn't fit in the stack space allocated to store only a base
class instance).  Generally, though, the language would prevent this.
In D this doesn't happen because emplace just gives you the class
instance as a reference (to a stack location, but nonetheless), and
reassignment just updates the reference, it doesn't actually overwrite
the base class instance.


T

-- 
If it tastes good, it's probably bad for you.


Re: Why are structs and classes so different?

2022-05-15 Thread Ali Çehreli via Digitalmars-d-learn

On 5/15/22 13:05, Kevin Bailey wrote:

> I've been programming in C++ full time for 32 years

Hi from an ex-C++'er. :) I managed to become at least a junior expert in 
C++ between 1996-2015. I don't use C++ since then.


I still think my answer is the real one. My implied question remains: 
Why does C++ have struct and class disticnction? I know they have 
different default access specifications but does that warrant two kinds?


I claim there are two types in C++ as well: value types and reference 
types. And types of an inheritance hirerachy are by convention reference 
types. As others reminded on this thread, C++ programmers follow 
guidelines to treat types of hierarchies as reference types.


> so I'm familiar
> with slicing. It doesn't look to me like there's a concern here.

Slicing renders types of class hierarchies reference types. They can't 
be value types because nobody wants to pass a Cat sliced as an Animal. 
It's always a programmer error.


All D does is (just like C# did) appreciate the differences between 
these two kinds and utilize existing keywords.


> One question is, how should we pass objects - by value or by reference?
> In C++, you can do either, of course, but you take your chances if you
> pass by value - both in safety AND PERFORMANCE.

D is very different from C++ when it comes to that topic:

- Since classes are reference types, there is no issue with performance 
whatsoever: It is just a pointer copy behind the scenes.


- Since structs are value types, they can be shallow-copied without any 
concern. (D disallows self-referencing structs.) Only when it matters, 
one writes the copy constructor or the post-blit. (And this happens very 
rarely.)


- rvalues are moved by default. They don't get copied at all. (Only for 
structs because classes don't have rvalues.)


> The bottom line is that
> no one passes by value, even for PODs (although we may return even large
> objects.)

I know it very well. In reality, nobody should care unless it matters 
semantically: Only if the programmer wants to pass an object by 
reference it should be done so. For example, to mutate an object or 
store a reference to it.


You must be familiar with the following presentation by Herb Sutter how 
parameter passing is a big problem. (Yet, nobody realizes until a 
speaker like Herb Sutter makes a presentation about it.)


  https://www.youtube.com/watch?v=qx22oxlQmKc=923s

Such concerns don't exist in D especially after fixing the "in 
parameters" feature. Semantically, the programmer should say "this is an 
input to this function". The programmer should not be concerned whether 
the number of bytes is over a threshold for that specific CPU or 
twhether the copy constructor may be expensive.


D does not have such issues. The programmer can do this:

- Compile with -preview=in

- Mark function parameters as in (the ones that are input):

auto foo(in A a, in B b) {
  // ...
}

The compiler should deal with how to pass parameters. The programmer 
provides the semantics and D follows these rules:


  https://dlang.org/spec/function.html#in-params

Although one of my colleagues advices me to not be negative towards C++, 
having about 20 years of experience with C++, I am confident C++ got 
this wrong and D got this right. D programmers don't write move 
constructors or move assignment. Such concepts don't even exist.


In summary, if a programmer has to think about pass-by-reference, that 
programmer has been conditioned to think that way. It has always been 
wrong. Passing by reference should have been about semantics. (Herb 
Sutter uses the word "intent" in that presentation.)


> But I asked a different question: Why can't I put a class object on the
> stack? What's the danger?

There is no danger. One way I like is std.typecons.scoped:

import std.stdio;
import std.typecons;

class C {
  ~this() {
writeln(__FUNCTION__);
  }
}

void main() {
  {
auto c = scoped!C();
  }
  writeln("after scope");
}

> Note that operating on that object hasn't changed. If I pass by
> reference, it's no different than if I had created a reference.

(Off-topic: I always wonder whether pass-by-reference comes with 
performance cost. After all, the members of by-reference struct will 
have to be accessed through a pointer, right? Shouldn't pass-by-value be 
faster for certain types? I think so but I never bothered to check the 
size threshold below which to confidently pass-by-value.)


> One might say, Well, if D creates by value, then it has to pass by
> value. But it doesn't; it has the 'ref' keyword.

That's only when one wants to pass a reference to an object. I blindly 
pass structs by-value. The reason is, I don't think any struct is really 
large to cost byte copying. It's just shallow copy and it works. (Note 
that there are not much copy constructors in D.)


> I hope Ali's answer isn't the real reason. I would be sad if D risked
> seg faults just to make class behavior "consistent".

I don't 

Re: Why are structs and classes so different?

2022-05-15 Thread Mike Parker via Digitalmars-d-learn

On Sunday, 15 May 2022 at 20:05:05 UTC, Kevin Bailey wrote:



One question is, how should we pass objects - by value or by 
reference? In C++, you can do either, of course, but you take 
your chances if you pass by value - both in safety AND 
PERFORMANCE. The bottom line is that no one passes by value, 
even for PODs (although we may return even large objects.)


Pass struct instances by ref or by value as needed, just as you 
do in C++.


For classes, you never have direct access to the instance. Your 
class reference is a handle (a pointer) that is always passed by 
value.




But I asked a different question: Why can't I put a class 
object on the stack? What's the danger?


I answered that one. You can put a class on the stack with 
`scope`. There is no danger in that.


If you're wanting direct access to the class instance, like you 
would have with a struct, you don't have that in D. Classes are 
modeled on Java, not C++.




Note that operating on that object hasn't changed. If I pass by 
reference, it's no different than if I had created a reference.


Again, you never have direct access to the object instance. You 
always access it through the handle.




One might say, Well, if D creates by value, then it has to pass 
by value. But it doesn't; it has the 'ref' keyword.


Everything is passed by value unless the `ref` keyword is present.



One might want to avoid passing by value accidentally. Ok, one 
could have D pass class objects by reference implicitly.


How do you pass by value accidentally? By forgetting the `ref` 
keyword?




I don't like things silently changing like that, so one might 
have D forbid all but pass by 'ref' or pointer for class 
objects.


I don't understand where you're coming from here. How can things 
silently change?




I hope Ali's answer isn't the real reason. I would be sad if D 
risked seg faults just to make class behavior "consistent".




Where is the risk of seg faults? Are you referring to the fact 
that class references are default initialized to null?





Re: Why are structs and classes so different?

2022-05-15 Thread Ola Fosheim Grøstad via Digitalmars-d-learn

On Sunday, 15 May 2022 at 20:05:05 UTC, Kevin Bailey wrote:
I've been programming in C++ full time for 32 years, so I'm 
familiar with slicing. It doesn't look to me like there's a 
concern here.


Yes, slicing is not the issue. Slicing is a problem if you do 
"assignments" through a reference that is typed as the 
superclass… so references won't help.


The original idea might have been that structs were value-types, 
but that is not the case in D. They are not, you can take the 
address…


So what you effectively have is that structs follow C layout 
rules and D classes are not required to (AFAIK), but there is an 
ABI and C++ layout classes, so that freedom is somewhat limited… 
D classes also have a mutex for monitors.


In an ideal world one might be tempted to think that classes were 
ideal candidates for alternative memory management mechanisms 
since they are allocated on the heap. Sadly this is also not true 
since D is a system level programming language and you get to 
bypass that "type characteristic" and can force them onto the 
stack if you desire to do so…








Re: Why are structs and classes so different?

2022-05-15 Thread Kevin Bailey via Digitalmars-d-learn

Hi Mike (and Guillaume, since you posted the same link),

Thanks for the long explanation.

I've been programming in C++ full time for 32 years, so I'm 
familiar with slicing. It doesn't look to me like there's a 
concern here.


There seem to be a couple different questions here. I suspect 
that you answered a different one than I asked.


One question is, how should we pass objects - by value or by 
reference? In C++, you can do either, of course, but you take 
your chances if you pass by value - both in safety AND 
PERFORMANCE. The bottom line is that no one passes by value, even 
for PODs (although we may return even large objects.)


But I asked a different question: Why can't I put a class object 
on the stack? What's the danger?


Note that operating on that object hasn't changed. If I pass by 
reference, it's no different than if I had created a reference.


One might say, Well, if D creates by value, then it has to pass 
by value. But it doesn't; it has the 'ref' keyword.


One might want to avoid passing by value accidentally. Ok, one 
could have D pass class objects by reference implicitly.


I don't like things silently changing like that, so one might 
have D forbid all but pass by 'ref' or pointer for class objects.


In any case, this doesn't quite address the instantiate by value 
issue.


If there's still a case where putting an object on the stack 
breaks, I would greatly appreciate seeing a few lines of example 
code.


I hope Ali's answer isn't the real reason. I would be sad if D 
risked seg faults just to make class behavior "consistent".


thx



Re: Why are structs and classes so different?

2022-05-15 Thread Ali Çehreli via Digitalmars-d-learn

On 5/15/22 08:26, Kevin Bailey wrote:

> structs and classes are so different.

I think a more fundamental question is why structs and classes both 
exist at all. If they could be the same, one kind would be sufficient. 
And the answer is there are value types and there are reference types in 
programming.


Ali



Re: Why are structs and classes so different?

2022-05-15 Thread Mike Parker via Digitalmars-d-learn

On Sunday, 15 May 2022 at 15:26:40 UTC, Kevin Bailey wrote:

I'm trying to understand why it is this way. I assume that 
there's some benefit for designing it this way. I'm hoping that 
it's not simply accidental, historical or easier for the 
compiler writer.


There's a problem that arises with pass-by-value subclasses 
called "object slicing". Effectively, it's possible to "slice 
off" members of superclasses. It's one of many pitfalls to be 
avoided in C++. There you typically find the convention of 
structs being used as POD (Plain Old Data) types (i.e., no 
inheritance) and classes when you want inheritance, in which case 
they are passed around to functions as references.


For reference:
https://stackoverflow.com/questions/274626/what-is-object-slicing

D basically bakes the C++ convention into the language, with a 
class system inspired by Java.




One problem that this causes is that I have to remember 
different rules when using them. This creates the additional 
load of learning and remembering which types are which from 
someone else's library.


Of all the complexity we need to remember as programmers, this is 
fairly low on the totem pole. Simple rule: if you need (or want) 
inheritance, use classes. If not, use structs.





A bigger problem is that, if I have a struct that I suddenly 
want to inherit from, I have to change all my code.


You should generally know up front if you need inheritance or 
not. In cases where you change your mind, you'll likely find that 
you have very little code to change. Variable declarations, sure. 
And if you were passing struct instances to functions, you'd want 
to change the function signatures, but that should be the lion's 
share of what you'd need to change. After all, D doesn't use the 
`->` syntax for struct pointers, so any members you access would 
be via the dot operator.


In addition to that work, in both of these cases, one could 
easily do it wrong:


// Fine with a struct, fatal with a class.
Foo foo;

At least in C++, the compiler would complain. In D, not even a 
warning.


You generally find out about that pretty quickly in development, 
though. That's a good reason to get into the habit of 
implementing and running unit tests, so if you do make changes 
and overlook something like this, then your tests will catch it 
if normal operation of the program doesn't.




Why is it this way? What is the harm of putting a class object 
on the stack?


I've answered the "why" above. As to the the second question, 
there's no harm in putting a class on the stack:


```d
import std.stdio;

class Clazz {
~this() { writeln("Bu-bye"); }
}

void clazzOnStack() {
writeln("Entered");
scope c = new Clazz;
writeln("Leaving");
}

void main()
{
clazzOnStack();
writeln("Back in main");
}
```

You'll find here that the destructor of `c` in `clazzOnStack` is 
called when the function exits, just as if it were a struct. 
`scope` in a class variable declaration will cause it to the 
class to be allocated on the stack.


Note, though, that `c` *still* a reference to the instance. You 
aren't manipulating the class instance directly. If you were to 
pass `c` to a function `doSomething` that accepts a `Clazz` 
handle, it makes no difference that the instance is allocated on 
the stack. `doSomething` would neither know nor care. `c` is a 
handle, so you aren't passing the instance directly and it 
doesn't matter where it's allocated.


There's more to the story than just reference type vs. value 
type. Structs have deterministic destruction, classes by default 
do not (`scope` can give it to you as demonstrated above). [See 
my blog post on the 
topic](https://dlang.org/blog/2021/03/04/symphony-of-destruction-structs-classes-and-the-gc-part-one/) for some info. (And I'm reminded I need to write the next article in that series; time goes by too fast).


Everyone has their own criteria for when to choose class and when 
to choose struct. For me, I default to struct. I consider 
beforehand if I need inheritance, and if yes, then I ask myself 
if I can get by without deterministic destruction. There are ways 
to simulate inheritance with structs, and ways to have more 
control over destruction with classes, so there are options 
either way.


Re: Why are structs and classes so different?

2022-05-15 Thread Guillaume Piolat via Digitalmars-d-learn

On Sunday, 15 May 2022 at 15:26:40 UTC, Kevin Bailey wrote:
I'm trying to understand why it is this way. I assume that 
there's some benefit for designing it this way. I'm hoping that 
it's not simply accidental, historical or easier for the 
compiler writer.


Perhaps someone more informed will chime in, but there is a 
reason to avoid object inheritance with value types, and force 
them to be reference types.


https://stackoverflow.com/questions/274626/what-is-object-slicing

If we want to avoid that problem, then object with inheritance 
and virtual functions have to be reference types.


But you still need values types. So now you have both struct and 
class, like in C# (Hejlsberg, 2000).


For an escape hatch, D has library ways to have structs with 
virtual functions (there is a DUB package for that), and classes 
on the stack (Scoped!T, RefCounted!T, a __traits).





Re: Why are structs and classes so different?

2022-05-15 Thread Alain De Vos via Digitalmars-d-learn

Can i summarize ,
structs are value-objects which live on the stack.
class instances are reference objects which live on the heap.


Why are structs and classes so different?

2022-05-15 Thread Kevin Bailey via Digitalmars-d-learn
I've done some scripting in D over the years but I never dug into 
D until recently. I'm going through Learning D and I was reminded 
that structs and classes are so different.


- struct methods are non-virtual while class methods are virtual
- Thus, structs can't inherit, because how would you find the 
child's destructor given a parent pointer?

- On the stack, structs by-value but classes are by-reference

I'm trying to understand why it is this way. I assume that 
there's some benefit for designing it this way. I'm hoping that 
it's not simply accidental, historical or easier for the compiler 
writer.


One problem that this causes is that I have to remember different 
rules when using them. This creates the additional load of 
learning and remembering which types are which from someone 
else's library.


A bigger problem is that, if I have a struct that I suddenly want 
to inherit from, I have to change all my code. In addition to 
that work, in both of these cases, one could easily do it wrong:


// Fine with a struct, fatal with a class.
Foo foo;

At least in C++, the compiler would complain. In D, not even a 
warning.


Why is it this way? What is the harm of putting a class object on 
the stack?