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."