October 10
On 10/10/2024 4:35 PM, Danni Coy wrote:
> As a new user of the language the only way you can figure out what it does is to read the documentation and memorise.

That's true with the entirety of programming languages. Though you don't really need to memorize - using it a few times will do the trick.

> That's something you want to avoid when creating new syntax if at all possible.

D uses ~ for string concatenation. Like a duck to water!

In any case, selection of syntax has many axes of constraints. Picking the best one is always a collection of compromises.

For example, every feature on your car is a collection of compromises. Even the "safety is our top priority" is inevitably false.

Even
```
a = b
```
is not intuitive. Assignment is quite different from algebraic equality. There's nothing inherent there about a copy vs a move, either.

It's all just what we learn and get used to.

October 11
They initialise an S from another S... I'm not sure what you mean by "they do different things"?

I've been wondering, why do you need to detect that a constructor is a copy or move constructor? That concept seems like a big internal hack. Why do normal overload selection semantics fail to determine the proper selection? It's not clear why it's useful or important that copy and move constructors are separate from all other constructors? They're not special; they just happen to accept a self-type as an init argument... the only reason I can think, is to identify the case where they're not present, and some copy/move code must by synthesised in lieu... what else hangs off that knowledge?

On Fri, 11 Oct 2024 at 16:11, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 10/10/2024 11:57 AM, Richard (Rikki) Andrew Cattermole wrote:
> > For whatever reason, this feature just keeps giving me the nagging
> sensation
> > that something isn't right with the motivation.
>
>
> Something I overlooked. With the original plan,
> ```
> this(ref S) // copy constructor
> this(S) // move constructor
> ```
>
> Generally speaking, functions that are overloaded should do the same
> thing, just
> with different arguments. But the copy and move overloads do something
> quite
> different. This is difference is what has been causing me frustrating
> problems
> with implementing it.
>
>


October 11
On Thu, 10 Oct 2024 at 17:10, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 10/8/2024 10:42 PM, Manu wrote:
> > Can you show us some cases?
>
> I'd get infinite recursion with overload resolution, because the compiler
> will
> try and match the argument to `S` and `ref S`, made even more complicated
> with
> rvalue references enabled.
>
> The compiler would go into infinite recursion converting a ref to an
> rvalue and
> back to a ref again. There were maybe 10 or more functions in the
> recursion
> stack, looping through the heavy semantic routines.
>
> Another problem was failure to compile libmir:
> ```
> source/mir/rc/ptr.d(395,15): Error: `mir.rc.ptr.castTo` called with
> argument
> types `(mir_rcptr!(C))` matches both:
>    source/mir/rc/ptr.d(275,13):     `mir.rc.ptr.castTo!(I,
> C).castTo(mir_rcptr!(C) context)`
>    and:
>    source/mir/rc/ptr.d(291,25):     `mir.rc.ptr.castTo!(I,
> C).castTo(immutable(mir_rcptr!(C)) context)`
>
> ```
> If libmir breaks, that means other code will break, too. I eventually
> decided it
> was not a good idea to modify existing semantics (rvalue constructors
> exist and
> are used already), as who knows how much breakage that would ensue. Adding
> distinct syntax for the new semantics seemed like a much more practical
> approach.
>
> I.e. `this(S)` already is accepted by the compiler and is used.
>
>
> > The story you present is incomplete, let me enhance:
> >
> > struct S { ... }
> > struct Other { ... }
> >
> > this(ref S) // copy constructor
> > this(this)  // postblit
> > this(S)     // move constructor
> > ~this()     // destructor
>
> It's not that simple. You can have:
>
> ```
> alias T = S;
> this(T);     // can't tell if move constructor by syntax
> ```
>
> > this(Other) // move from other (Other is an rvalue here)
> > this(ref Other) // copy from other (Other is obviously a ref)
>
> Problems show up when `Other` is implicitly convertible to `S`.
>

If the overload resolution rules don't prefer an exact match over a conversion, then something is very wrong with the overload selection rules. What situations cause those waters to become murky?

But, I think you actually missed my point here; I provided a ref and non-ref overload for Other... how do I move-construct from some OTHER type? Move constructors can't just be for S s = otherS; that's obviously an important case, but it excludes an enormous range of the "move semantics" landscape.

The fact you're talking about a separate constructor type to identify the
move case leads me to suspect that the rvalue semantic only applies to the
argument to that particular function and nothing else?
That's not really "move semantics", that's just a move constructor... and I
think I've completely misunderstood your DIP if this is correct?

> Likewise, I don't understand why opMove would be necessary to distinguish from
> > opAssign?
>
> Is `a = b` a move or a copy?
>

is `b` an rvalue or an lvalue? (or had some special sauce tag it with a 'last-use' flag or anything like that)

> If you introduce opMove, then you'll end up with opIndexMove, opOpMove, etc.
> > Is that something you want? Assuming you want to avoid this; then I also
> imagine
> > solving for that issue would equally solve for the main constructor case?
>
> This is why I don't like implicit conversions for a struct - you wind up
> with
> impossible tangles of meaning. Oh, and rvalue to ref conversions, too.
>

I don't feel like I have these problems in C++, which is far less constrictive. I have no way to know or assess whether any of the arrangements of rules you considered or tried actually arrange into a nice tight lattice or not... or if they were generally cohesive and well-formed. We're expected to accept that you tried every possible reasonable arrangement and conclusively determined that it's not workable. I have no idea how we can assess that for reality or not... :/


October 11
>
> Aside from whatever Walter's reasoning is, there are existing constructors in the wild which use
>
>     this(S)
>

I had a moment to think on this case you present.
It's not clear to me how it actually works now; but I think it would be
interesting to explore that first; because I think the solution for this
case being reinterpreted as move-constructor is related (and equally
solved).

This function receives it's arg BY-VALUE; so it can happily accept an
r-value... but it's not actually clear how this function receives an lvalue
even in today's language?
In order for this function to receive an l-value by-value, it must first
make a copy to supply as the function's argument (but the function is
allowed to internally mutate its argument)..

...but isn't *this* said to be the copy constructor?

This already looks like a chicken/egg problem in today's language... how
does it create a copy of an lvalue to pass the argument to this function
without calling this function?
Whatever answer exists that makes this work equally makes the case work
where this is reinterpreted as a move-constructor.

The only explanation I can imagine is; because this ISN'T actually a copy
constructor, the compiler determined that it was appropriate to synthesise
one (which would be to just copy all members), and so an implicit copy
constructor DOES actually exist beside this constructor, you just didn't
write it.
And assuming that's the case, when this becomes reinterpreted as a move
constructor, the code remains perfectly correct!

I don't see how elevating this declaration to a move-constructor actually
changes the semantics at all... I don't think it does? I think this
function ALREADY IS A MOVE CONSTRUCTOR (at least; it's an
rvalue-constructor, which is the same thing from a semantic perspective);
it's just working with an inefficient calling convention.
Move semantics as proposed simply improve this code, no?


October 11
On Thu, 10 Oct 2024 at 17:56, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> BTW, every reply you make starts a separate subthread. Can you please fix that?
>

I use gmail, which is BY FAR the world's most popular email service, by a
country mile.
I have default settings. I've never even opened the settings window, I
wouldn't know where to find it or what it looks like.
I press the reply button, type words, and press send.

If this doesn't work, then the bug is in the custom forum software. I suggest you address a bug report to the author.

I can't seriously be the only person here that uses gmail? ...if that's true; that really says something about the ambient autism score in this room...


October 11
On Friday, October 11, 2024 1:35:44 AM MDT Manu via Digitalmars-d wrote:
> On Thu, 10 Oct 2024 at 17:56, Walter Bright via Digitalmars-d <
>
> digitalmars-d@puremagic.com> wrote:
> > BTW, every reply you make starts a separate subthread. Can you please fix that?
>
> I use gmail, which is BY FAR the world's most popular email service, by a
> country mile.
> I have default settings. I've never even opened the settings window, I
> wouldn't know where to find it or what it looks like.
> I press the reply button, type words, and press send.
>
> If this doesn't work, then the bug is in the custom forum software. I suggest you address a bug report to the author.
>
> I can't seriously be the only person here that uses gmail? ...if that's true; that really says something about the ambient autism score in this room...

Well, gmail is known to screw up mailing lists, and they do not care (in particular, they do not send you your own replies, which can screw up threading - especially when you want to reply to your own messages). So, it wouldn't surprise me at all if gmail is doing something wrong that's screwing things up. I guess that they think that too few people use mailing lists for them to care (and admittedly, it probably is a very small percentage of their users who are on mailing lists).

Part of the reason that I stopped using gmail ages ago was because of how badly they handle mailing lists.

All that being said, the threading for your replies seems to be working correctly both in my e-mail client and on the forums. So, I don't know why Walter is having issues with them. IIRC, he uses the newsgroup interface via Thunderbird, but since the newsgroup is the actual thing that holds all of the data, and the mailing list and forum are built on top of it, I would think that the newsgroup would be threading things correctly if the other stuff is. So, I have no clue what the problem is.

- Jonathan M Davis



October 11
On Friday, October 11, 2024 1:25:53 AM MDT Manu via Digitalmars-d wrote:
> > Aside from whatever Walter's reasoning is, there are existing constructors in the wild which use
> >
> >     this(S)
>
> I had a moment to think on this case you present.
> It's not clear to me how it actually works now; but I think it would be
> interesting to explore that first; because I think the solution for this
> case being reinterpreted as move-constructor is related (and equally
> solved).
>
> This function receives it's arg BY-VALUE; so it can happily accept an
> r-value... but it's not actually clear how this function receives an lvalue
> even in today's language?
> In order for this function to receive an l-value by-value, it must first
> make a copy to supply as the function's argument (but the function is
> allowed to internally mutate its argument)..
>
> ...but isn't *this* said to be the copy constructor?
>
> This already looks like a chicken/egg problem in today's language... how
> does it create a copy of an lvalue to pass the argument to this function
> without calling this function?
> Whatever answer exists that makes this work equally makes the case work
> where this is reinterpreted as a move-constructor.
>
> The only explanation I can imagine is; because this ISN'T actually a copy
> constructor, the compiler determined that it was appropriate to synthesise
> one (which would be to just copy all members), and so an implicit copy
> constructor DOES actually exist beside this constructor, you just didn't
> write it.
> And assuming that's the case, when this becomes reinterpreted as a move
> constructor, the code remains perfectly correct!
>
> I don't see how elevating this declaration to a move-constructor actually
> changes the semantics at all... I don't think it does? I think this
> function ALREADY IS A MOVE CONSTRUCTOR (at least; it's an
> rvalue-constructor, which is the same thing from a semantic perspective);
> it's just working with an inefficient calling convention.
> Move semantics as proposed simply improve this code, no?

No, as far as the language is concerned, it's not a copy constructor, but it also isn't necessarily a move constructor. In the case that I've described, it might work as a move constructor, but there's no guarantee that all constructors with that signature would. Also, the constructor is question is likely to be a template, which will then probably not play well with what Walter is trying to do (e.g. copy constructors can't currently be templated, because that doesn't work unless copy constructors have a unique syntax, which they don't). As Razvan explained in this thread, in order for copy constructors (or move constructors) to be allowed to be templates, they really need a distinct syntax, otherwise the compiler can't know that they are in fact copy constructors or move constructors until they're instantiated, which causes a variety of issues.

Honestly, as a user, I see _zero_ benefit in trying to treat either copy constructors or move constructors like they're normal constructors. They are treated as special by the compiler in a variety of ways, and I want them to be visually distinct so that it's clear that they exist. It would also be nice if the compiler gave me an error when I screwed up a copy constructor or move constructor in a way that it wouldn't work as one for whatever reason (e.g. getting ref wrong by accident). A big part of the problem with the current copy constructors is that it really isn't easy to see at a glance which constructors are copy constructors. And the fact that they can't be templated is a _huge_ problem, particularly with regards to attributes.

While the syntax =this() isn't necessarily great (personally, I'd probably just vote for using @move for move constructors and then change it so that copy constructors should have @copy), I see nothing but problems from not making copy and move constructors distinct and obvious.

- Jonathan M Davis



October 11
On Friday, October 11, 2024 12:48:45 AM MDT Manu via Digitalmars-d wrote:
> They initialise an S from another S... I'm not sure what you mean by "they do different things"?
>
> I've been wondering, why do you need to detect that a constructor is a copy or move constructor? That concept seems like a big internal hack. Why do normal overload selection semantics fail to determine the proper selection? It's not clear why it's useful or important that copy and move constructors are separate from all other constructors? They're not special; they just happen to accept a self-type as an init argument... the only reason I can think, is to identify the case where they're not present, and some copy/move code must by synthesised in lieu... what else hangs off that knowledge?

Another example of how they're treated as special is that if you declare no constructors for a struct, you get implicit construction syntax for that type. e.g.

    struct S
    {
        int x;
        int y;
    }

    void main()
    {
        auto s1 = S(42);
        auto s2 = S(42, 27);
    }

As soon as you declare any constructors, you no longer get that implicit construction syntax, and you can only construct the type with the constructors that you provided - except that copy constructors (and eventually move constructors) are not treated as constructors in this context. Declaring a copy constructor does not get rid of the implicit construction syntax. I've specifically had to deal with this situation when wrapping D code in order to create the same constructors that the D type has but in another language, and the fact that a copy constructor is __ctor just like all of the other constructors except for postblit constructors makes this more of a pain for no good reason.

It's also the case that some metaprogramming needs to know whether a type has a copy constructor or move constructor in order to do the right thing (e.g. the lowerings for assigning slices of arrays to each other have to take that into account as do types like std.typecons.Nullable). Hiding them as normal constructors just makes that harder.

All in all, I don't understand why you want to treat copy constructors or move constructors as if they were normal constructors. Unlike normal constructors, they get used when you do not call them, and you usually don't call them at all. They also have a huge impact on the semantics of how a type is actually used in many situations in a way that normal constructors do not. Treating them as normal constructors makes it harder for the user to see what's going on, and anyone generating code via metaprogramming then has a harder time doing that, because detecting them is more complicated.

What benefit do you see in treating copy constructors or move constructors like they're normal constructors instead of explicitly treating them as special - especially given that the compiler already has to treat them as special in order to generate the correct code in many cases?

- Jonathan M Davis



October 11
On Friday, 11 October 2024 at 06:25:02 UTC, Walter Bright wrote:
> In any case, selection of syntax has many axes of constraints. Picking the best one is always a collection of compromises.
>
> For example, every feature on your car is a collection of compromises. Even the "safety is our top priority" is inevitably false.
>
> Even
> ```
> a = b
> ```
> is not intuitive. Assignment is quite different from algebraic equality. There's nothing inherent there about a copy vs a move, either.
>
> It's all just what we learn and get used to.

Suppose everything goes well and we continue on our way with the best option. Will we be able to know by looking at a flag that the object has been moved before?

SDB@79
October 11
On 10/11/24 09:35, Manu wrote:
> 
> If this doesn't work, then the bug is in the custom forum software.

NNTP is not particularly custom.

> I suggest you address a bug report to the author.

FWIW I am getting your replies in threads both in Thunderbird and on forum.dlang.org, so the issue might be related to Walter's viewer specifically.