March 29, 2018
On Thursday, 29 March 2018 at 20:05:48 UTC, Rubn wrote:
> On Thursday, 29 March 2018 at 19:11:30 UTC, Dgame wrote:
>> Just to be sure it does not got los: You know that you can avoid the temp/copy if you add one method to your struct, yes?
>>
>> ----
>> import std.stdio;
>>
>> struct Big {
>>     string name;
>>     float[1000] values;
>>
>>     this(string name) {
>>         this.name = name;
>>     }
>>
>>     @disable
>>     this(this);
>>
>>     ref auto byRef() inout {
>>         return this;
>>     }
>> }
>>
>> void foo(ref const Big b) {
>>     writeln(b.name);
>> }
>>
>> void main() {
>>     Big b = Big("#1");
>>     foo(b);
>>     foo(Big("#2").byRef);
>> }
>> ----
>>
>> That works like a charm and avoids any need for rvalue references. Just add it as mixin template in Phobos or something like that.
>
> Doesn't work with built-in types like float.

Why would you want to use a float as a rvalue reference?

> Just adds bloat for operators like opBinary if you want that to be ref.
>
> foo((a.byRef + b.byRef * c.byRef).byRef)
>
> // vs
>
> foo(a + b * c);
>
> It's kind of funny all this talk about allowing temporaries to bind to refs being messy, yet you can already bind a temporary to a ref in a messy way using that.

Yeah, it is a bit messy. It is not perfect, but is does avoid any temp var!
Let's look at a Vector2f example with the following opBinary:

----
auto opBinary(string op)(ref const Vector2f v) {
    return Vector2f(mixin("this.x" ~ op ~ "v.x"), mixin("this.y" ~ op ~ "v.y"));
}

void foo(ref const Vector2f v) {
    writeln(v.x, ':', v.y);
}

foo((Vector2f(2, 4).byRef + Vector2f(4, 6).byRef).byRef);
----

Since opBinary needs to be a template, you can combine my solution with auto ref:

----
auto opBinary(string op)(auto ref const Vector2f v) {
    return Vector2f(mixin("this.x" ~ op ~ "v.x"), mixin("this.y" ~ op ~ "v.y"));
}

void foo(ref const Vector2f v) {
    writeln(v.x, ':', v.y);
}

foo((Vector2f(2, 4) + Vector2f(4, 6)).byRef);
----

That is cleaner. :)
March 30, 2018
On Thursday, 29 March 2018 at 20:22:47 UTC, Dgame wrote:
> On Thursday, 29 March 2018 at 20:05:48 UTC, Rubn wrote:
>> On Thursday, 29 March 2018 at 19:11:30 UTC, Dgame wrote:
>>> Just to be sure it does not got los: You know that you can avoid the temp/copy if you add one method to your struct, yes?
>>>
>>> ----
>>> import std.stdio;
>>>
>>> struct Big {
>>>     string name;
>>>     float[1000] values;
>>>
>>>     this(string name) {
>>>         this.name = name;
>>>     }
>>>
>>>     @disable
>>>     this(this);
>>>
>>>     ref auto byRef() inout {
>>>         return this;
>>>     }
>>> }
>>>
>>> void foo(ref const Big b) {
>>>     writeln(b.name);
>>> }
>>>
>>> void main() {
>>>     Big b = Big("#1");
>>>     foo(b);
>>>     foo(Big("#2").byRef);
>>> }
>>> ----
>>>
>>> That works like a charm and avoids any need for rvalue references. Just add it as mixin template in Phobos or something like that.
>>
>> Doesn't work with built-in types like float.
>
> Why would you want to use a float as a rvalue reference?

In templates to avoid template bloat with auto ref.

>> Just adds bloat for operators like opBinary if you want that to be ref.
>>
>> foo((a.byRef + b.byRef * c.byRef).byRef)
>>
>> // vs
>>
>> foo(a + b * c);
>>
>> It's kind of funny all this talk about allowing temporaries to bind to refs being messy, yet you can already bind a temporary to a ref in a messy way using that.
>
> Yeah, it is a bit messy. It is not perfect, but is does avoid any temp var!
> Let's look at a Vector2f example with the following opBinary:
>
> ----
> auto opBinary(string op)(ref const Vector2f v) {
>     return Vector2f(mixin("this.x" ~ op ~ "v.x"), mixin("this.y" ~ op ~ "v.y"));
> }
>
> void foo(ref const Vector2f v) {
>     writeln(v.x, ':', v.y);
> }
>
> foo((Vector2f(2, 4).byRef + Vector2f(4, 6).byRef).byRef);
> ----
>
> Since opBinary needs to be a template, you can combine my solution with auto ref:
>
> ----
> auto opBinary(string op)(auto ref const Vector2f v) {
>     return Vector2f(mixin("this.x" ~ op ~ "v.x"), mixin("this.y" ~ op ~ "v.y"));
> }
>
> void foo(ref const Vector2f v) {
>     writeln(v.x, ':', v.y);
> }
>
> foo((Vector2f(2, 4) + Vector2f(4, 6)).byRef);
> ----
>
> That is cleaner. :)

Just adding more template bloat and it is still messy, it's not like we are trying to find a work around to a problem. A solution already exists, the cleaner syntax you'd get with rvalue references (along with better compatibility with C++ and other benefits) can't be beat.

March 29, 2018
On 03/23/2018 09:06 PM, Jonathan M Davis wrote:
> 
> My biggest concern in all of this is that I don't want to see ref start
> accepting rvalues as has been occasionally discussed. It needs to be clear
> when a function is accept an argument by ref because it's going to mutate
> the object and when it's accepting by ref because it wants to avoid a copy.
> 

That ship sailed ages ago: It's already unclear. If we want to fix that, we can fix it, but blocking rvalue ref does nothing for that cause.
March 29, 2018
On 03/24/2018 12:03 AM, Manu wrote:
>> Why aim for "it often works", when we want "it always works"? Forcing const
>> upon people who want to pass rvalues by reference is just not good enough.
>> It is bad language design.
> 
> I think you need to re-read the whole thread, and understand what
> we're even talking about.
> Nobody wants to pass rvalues by mutable-ref... 

I do. The ban serves no useful purpose (at least not any purpose that D doesn't *already* fail at - like knowing at the callsite whether a param is intended to be mutated). And it would permit the "avoid a copy" benefits in a completely orthogonal way - *without* relying on the param fitting D's transitive const requirements.
March 29, 2018
On Thursday, March 29, 2018 23:28:54 Nick Sabalausky  via Digitalmars-d wrote:
> On 03/23/2018 09:06 PM, Jonathan M Davis wrote:
> > My biggest concern in all of this is that I don't want to see ref start accepting rvalues as has been occasionally discussed. It needs to be clear when a function is accept an argument by ref because it's going to mutate the object and when it's accepting by ref because it wants to avoid a copy.
> That ship sailed ages ago: It's already unclear. If we want to fix that, we can fix it, but blocking rvalue ref does nothing for that cause.

Really? And how often does ref get used just to avoid copying? You can almost always look at a function, see that it accepts ref, and know that it's supposed to mutate the argument. Functions that want to avoid copying lvalues usually use auto ref, not ref, whereas if ref accepted rvalues, a number of folks would start using it all over the place to avoid copying. Right now, folks rarely use ref that way, because it becomes too annoying to call the function with an rvalue. So, while it might not be the case 100% of the time right now that ref is used with the purpose of mutating the argument, it almost always is. As such, you can pretty reliably look at a function signature and expect that if one of its parameters is ref, it's going to be mutating that argument. The function that accepts an argument by ref with no intention of mutating it is very much the exception, and I really don't want to see that change.

- Jonathan M Davis

March 30, 2018
On Tuesday, 27 March 2018 at 18:14:18 UTC, Manu wrote:
> On 27 March 2018 at 00:14, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>> On Monday, 26 March 2018 at 19:24:13 UTC, Manu wrote:
>>>
>>> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>>
>>>> On Friday, 23 March 2018 at 22:01:44 UTC, Manu wrote:
>>>>>

>> That's _if_ T is big and _if_ it even gets copied,
>
> You've just described the exact anatomy of a ref function!
> You wouldn't write a function to receive T by ref UNLESS T was both
> big, and the function probably won't inline (therefore definitely
> copy), and that condition will be triggered by any of the list of
> reasons I've said a bunch of times (extern, dll, lib, virtual, etc).
> People don't just love writing ref (well, some people might), but they
> use it deliberately, typically in user-facing boundary API's for these
> exact reasons.

I know. I was arguing that those cases are uncommon and the API pain is therefore not too big of an issue. I'm pretty sure that you feel it more because of what you write in D.

> Right. I'm talking about deliberate use of ref... Or *existing*
> (deliberate) use of ref, as is the case in almost all my my cases. The
> code already exists.

Right, and I was assuming (perhaps incorrectly) that this existing code was C++, hence me being on board with binding rvalues to const ref there.

> Only if you ARE moving, and not copying. D must deep copy too if you
> actually copy.
> Your example assumes C++ doesn't have a move constructor. D has
> implicit move semantics, so you can only make an equivalent comparison
> where C++ also defines the move constructor so the move case doesn't
> pollute the ref comparison.

I wasn't assuming the lack of a move constructor. What I was saying is that passing by value in C++ will usually mean a copy, whereas in D it usually means a move.

> Also, irrespective of whether move semantics are performed (eliding
> potential deep copying, as in your example), the binary memcpy still
> has to be performed when handling values by-val, unless RVO (we're not
> talking about return values), or inlining is able to eliminate it.

Good point about memcpy.

>>> In C++'s case, it's not that references were deficient at being references that C++ needed rval-references, it's that references were deficient at being move-able.
>>
>>
>> There were deficient at being moveable because temporaries can bind to const T&.
>
> ... what? That's just not true at all.
> If temporaries couldn't bind to C++ ref, then you *definitely*
> wouldn't be able to move it, because you can guarantee that someone
> else owns the reference.

Precisely. That's how D works.


> rvalue references were introduced in C++ to capture the calls with
> rvalues into a separate function call, exactly the same way as the
> by-value overload will catch the rvalues in D (and perform an implicit
> move).

Yes.

> It was impossible for C++ to implement D's implicit move semantics
> when receiving by-value for reasons that have nothing to do with
> references; C++ allows interior pointers which breaks implicit moving,
> C++ can overload default constructor which also breaks implicit moving
> (no implicit way to reset the state of the prior owner).

I hadn't thought about the implications of interior pointers. Very good point.

> References have no interaction with move semantics.

Even given interior pointers, I disagree.

> But again, we're not talking about move semantics here, we're just talking about references ;)

See comment above ;)

>> I'd love to know what that would look like.
>
> That's exactly what I've been saying. For like, 9 years..
> It looks like this:
> https://github.com/TurkeyMan/DIPs/blob/ref_args/DIPs/DIP1xxx-rval_to_ref.md
>  (contribution appreciated)

I was unaware of this (or I forgot). After reading it I'm not sure of what corner cases might arise, but if I'm getting it right I think it could work.

> And as far as I
> can tell, it basically only affects me, because I do so much work
> against established C++ code! >_<

That's entirely possible. I can use my fingers to count the number of times I've written `extern(C++)`.

Atila


March 30, 2018
On Wednesday, 28 March 2018 at 16:15:53 UTC, Manu wrote:
> I discussed that in that document. I'm happy to remove const, but it requires a value judgement on the meaning of non-const in this case. It becomes controversial without const, but I'm personally happy to remove it if you can make The argument in favour. Can you give me some ideas where it would be useful?

>doesn't make sense because the output would be immediately discarded; such a function call given an rvalue as argument likely represents an accidental mistake on the users part, and we can catch that invalid code.

Most obvious:

void Close(ref HANDLE h)
{
  CloseHandle(h);
  h=INVALID_HANDLE;
}

If you want to take input argument, then of course mark it as input, but if not then not. Why the callee would need to care if the argument is rvalue or not? The only criticism against rvalue references I saw was when the reference outlives the temporary.
March 30, 2018
On 29 March 2018 at 21:08, Jonathan M Davis via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Thursday, March 29, 2018 23:28:54 Nick Sabalausky  via Digitalmars-d wrote:
>> On 03/23/2018 09:06 PM, Jonathan M Davis wrote:
>> > My biggest concern in all of this is that I don't want to see ref start accepting rvalues as has been occasionally discussed. It needs to be clear when a function is accept an argument by ref because it's going to mutate the object and when it's accepting by ref because it wants to avoid a copy.
>> That ship sailed ages ago: It's already unclear. If we want to fix that, we can fix it, but blocking rvalue ref does nothing for that cause.
>
> Really? And how often does ref get used just to avoid copying? You can almost always look at a function, see that it accepts ref, and know that it's supposed to mutate the argument. Functions that want to avoid copying lvalues usually use auto ref, not ref, whereas if ref accepted rvalues, a number of folks would start using it all over the place to avoid copying. Right now, folks rarely use ref that way, because it becomes too annoying to call the function with an rvalue. So, while it might not be the case 100% of the time right now that ref is used with the purpose of mutating the argument, it almost always is. As such, you can pretty reliably look at a function signature and expect that if one of its parameters is ref, it's going to be mutating that argument. The function that accepts an argument by ref with no intention of mutating it is very much the exception, and I really don't want to see that change.

Interesting. Just understand that you're trading that feeling for a
suite of edge cases though. Accepting asymmetric calling rules is a
pretty big cost to pay for that 'nice thought'.
That idea also dismisses the existence of the set of cases where ref
is genuinely useful/correct. Your sentiment effectively puts those use
cases into the position of 2nd-class citizens.
I understand your sentiment, but I think the cost is not balanced, or
even particularly fair with respect to users in those niche groups :/
March 30, 2018
On 30 March 2018 at 02:06, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Tuesday, 27 March 2018 at 18:14:18 UTC, Manu wrote:
>>
>> On 27 March 2018 at 00:14, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>
>>> On Monday, 26 March 2018 at 19:24:13 UTC, Manu wrote:
>>>>
>>>>
>>>> On 26 March 2018 at 07:40, Atila Neves via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>>>>
>>>>>
>>>>> On Friday, 23 March 2018 at 22:01:44 UTC, Manu wrote:
>>>>>>
>>>>>>
>
>>> That's _if_ T is big and _if_ it even gets copied,
>>
>>
>> You've just described the exact anatomy of a ref function!
>> You wouldn't write a function to receive T by ref UNLESS T was both
>> big, and the function probably won't inline (therefore definitely
>> copy), and that condition will be triggered by any of the list of
>> reasons I've said a bunch of times (extern, dll, lib, virtual, etc).
>> People don't just love writing ref (well, some people might), but they
>> use it deliberately, typically in user-facing boundary API's for these
>> exact reasons.
>
>
> I know. I was arguing that those cases are uncommon and the API pain is therefore not too big of an issue. I'm pretty sure that you feel it more because of what you write in D.

Totally.
But I think it's uncommon at least in-part because of this
inconvenience. It's at least a little bit circular. Even you're saying
above "yeah, just do by-val, it's *probably* insignificant"... you
only say that because you've allowed this inconvenience to change your
behaviour. And I think that's to be expected.
Other reasons that it's not common:
extern(C++) is not common.
OOP (virtuals) in D are fairly uncommon. We don't OOP much in D.
Closed-source (binary lib) distribution is uncommon... possibly
unheard of? (yet!)
DLL's are still problematic onvarious fronts, and I don't think they
enjoy anywhere near as much use as they deserve.

Most of these aren't desirable... we'd like to see more commercial
(closed source?) users, DLL's should be more popular than they are...
and more extern(C++) means more people using a key development
investment in D.
So yeah, I agree it's relatively uncommon if you don't interact with
one of the niches where it tends to be common! :P


>> Right. I'm talking about deliberate use of ref... Or *existing* (deliberate) use of ref, as is the case in almost all my my cases. The code already exists.
>
>
> Right, and I was assuming (perhaps incorrectly) that this existing code was C++, hence me being on board with binding rvalues to const ref there.

Right. But I'm not a fan is just rearranging the edge cases by
limiting it to a different set of cases and not applying it generally.
This also interacts with meta constructions, and while there are ANY
cases where calling doesn't just work as usual and it needs special
case handling, meta will still need static-if's to handle those cases.
We're just changing the criteria for the 'if'.
By allowing fully symmetric function calling rules, only then does the
noisy case-handling logic disappear from meta.


>> Only if you ARE moving, and not copying. D must deep copy too if you
>> actually copy.
>> Your example assumes C++ doesn't have a move constructor. D has
>> implicit move semantics, so you can only make an equivalent comparison
>> where C++ also defines the move constructor so the move case doesn't
>> pollute the ref comparison.
>
> I wasn't assuming the lack of a move constructor. What I was saying is that passing by value in C++ will usually mean a copy, whereas in D it usually means a move.

Right. But we're not talking about move's, we're talking about
NOT-move's (ie, preventing copies by passing by ref) ;)
Despite the appearance that we might be talking about rvalues, we're
actually talking about passing lvalues by ref... that's WHY you write
a function to accept its args by ref; to prevent deep copies when
passing lvalues.
The issue is, it's super-common to call functions with rvalues, and
the edge cases created by using ref are annoying and asymmetric, hence
such functions should receive rvalues too.


>>>> In C++'s case, it's not that references were deficient at being references that C++ needed rval-references, it's that references were deficient at being move-able.
>>>
>>> There were deficient at being moveable because temporaries can bind to const T&.
>>
>> ... what? That's just not true at all.
>> If temporaries couldn't bind to C++ ref, then you *definitely*
>> wouldn't be able to move it, because you can guarantee that someone
>> else owns the reference.
>
> Precisely. That's how D works.

Sorry, I'm lost now. I don't understand your initial point. You
created a relationship between ref's accepting rvalues, and the reason
that C++ introduced rvalue-references.
No such relationship exists... and I was trying to show that your
reasoning was inverted.


>> rvalue references were introduced in C++ to capture the calls with rvalues into a separate function call, exactly the same way as the by-value overload will catch the rvalues in D (and perform an implicit move).
>
> Yes.
>
>> References have no interaction with move semantics.
>
> Even given interior pointers, I disagree.

Please explain how there's any relationship between ref functions and move semantics?

C++/D's mechanism for catching the move case is different, but that
still doesn't affect or interact with the ref case.
ref accepting rvalues has absolutely no effect on move semantics,
either theoretically in D, or practically in C++. It's a totally
separate problem space.


>> But again, we're not talking about move semantics here, we're just talking about references ;)
>
> See comment above ;)

Likewise :P


>>> I'd love to know what that would look like.
>>
>>
>> That's exactly what I've been saying. For like, 9 years.. It looks like this:
>>
>> https://github.com/TurkeyMan/DIPs/blob/ref_args/DIPs/DIP1xxx-rval_to_ref.md
>>  (contribution appreciated)
>
> I was unaware of this (or I forgot). After reading it I'm not sure of what corner cases might arise, but if I'm getting it right I think it could work.

Right. So does that admission dismiss your points above where you
suggest there's some reason that C++ ref functions accepting rvalues
lead to T&&?
As far as I can tell from your prior posts, your only resistance is
the idea of a 'slippery slope' that leads to T&& appearing in D? I
promise, I'm just as concerned that never happens as you :)
If you can find a connection, I want to know about it. But I'm
confident that no such thing exists..


>> And as far as I
>> can tell, it basically only affects me, because I do so much work
>> against established C++ code! >_<
>
>
> That's entirely possible. I can use my fingers to count the number of times I've written `extern(C++)`.

Exactly.
I'd like to think that my set of use cases are not cases that we want
to discourage however. I'd like to see more users like myself in the
future...
March 30, 2018
On 30 March 2018 at 02:47, Kagamin via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> The only criticism against rvalue references I saw was when the reference outlives the temporary.

That's the only *technical* criticism I've ever heard too. Fortunately, D now has mechanisms for preventing that; D's @safety story with respect to reference lifetime seems to be solid now. 'return ref', and enforcing ref pointers don't escape the callee cleared to the road to allow accepting temporaries safety.