May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Thu, 09 May 2013 20:38:47 -0400, Manu <turkeyman@gmail.com> wrote:
> What were the arguments again against ref const()? You're talking about
> making it clear that the function isn't planning on mutating the given
> rvalue...
> I understand that const is stronger than C++, but is it actually a
> deal-breaker? It's the most logical fit here.
the counter-argument goes something like this:
struct VeryLarge
{
int[10] buffer;
VeryLarge *next;
}
So let's say you build a VeryLarge and return it, on the stack. Return by value.
VeryLarge buildOne(someArguments);
OK, you now want to assign it to a property:
class X
{
private VeryLarge _vl;
@property void vl(ref VeryLarge otherValue) { _vl = otherValue;}
}
X x = new X;
x.vl = buildOne(...);
If we make otherValue const, then we can't assign because of the indirection.
It's a tenuous argument, and I may not have made it in the best way, but the bottom line is that const is overly restrictive in this case. We're passing by ref because we don't want to incur the copy penalty *twice*. If we make it const, we've added an incorrect restriction.
The solution, ironically, is to take VeryLarge by value as an overload. This will simply do a move, and since it's already on the stack, no extra copy is made.
So we NEED it to be mutable, and we don't want to restrict ourselves from accepting rvalues.
So the above works fine as ref, for r and l values, because we are just trying to copy the data. It's when you specifically are passing by ref to modify the data that you want to reject rvalues.
As the original post in this thread pointed out, it's the way a library can alter another author's intention that causes problems.
I have another idea, but I need to put it at the top so it's not lost :)
-Steve
|
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 5/9/13 6:09 PM, Jonathan M Davis wrote:
> On Friday, May 10, 2013 07:35:36 Manu wrote:
>> I don't think this is entirely true, auto ref is a template concept, that
>> is, "automatic ref-ness", it selects the ref-ness of the argument
>> automatically, at compile time, just like auto applied everywhere else
>> (selects a type for instance, at compile time). This concept doesn't make
>> any sense applied to a non-template. It *IS* a ref as specified by the
>> programmer, there's nothing 'automatic' about it.
>
> I don't buy this at all. The entire point of auto ref on parameters was to say
> that you wanted to accept both rvalues and lvalues efficiently. The fact that
> the template implementation happened to forward refness as a result was a
> happy accident. auto ref is already described in TDPL, and it has nothing to
> do with templates there. Using auto ref on non-templated functions would be
> completely in line with what TDPL describes and would implement another
> feature from TDPL that we're currently missing.
Exactly. I'll add that just saying "auto is a template concept" is really devoid of information. It really is meaningless because "concept" is so broad a word. One may as well say "class is an abstraction notion" or any of a large variety of similar constructs.
Andrei
|
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sun, 05 May 2013 01:49:42 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > 2. Code evolution. > > Jonathan mentioned this too. The problem here is that as code evolves, meaningful code doing real work becomes silently useless code that patently does nothing. Consider: > > class Collection(T) { > ref T opIndex(size_t i) { ... } > ... > } > > void fix(ref double x) { if (isnan(x)) x = 0; } > > void fixAll(Collection!double c) { > foreach (i; 0 .. c.length) { > fix(c[i]); > } > } > > As design evolves, Collection's opIndex may change to return a T instead of ref T (e.g. certain implementations of sparse vectors). When that happens, the caller code will continue to compile and run. However, it won't do anything interesting: fix will be always called against a temporary plucked from the collection. What about specifying ref at the call site when you know you want the data modified? fix(ref c[i]); Then if c decides to start returning by value, this is a compiler error. IMO, fix really should take a pointer. But we want to avoid pointers due to the danger of them. so this is like applying & but keeps it safe. -Steve |
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > On 5/9/13 4:36 PM, Peter Alexander wrote: >> I'm not sure about how common it is, but I really don't like the idea of >> calls like swap(1, 2) being legal. Seems like a step backward from C++. > > I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language. Depends on context. int swap(int diskNum, int partitionNum); Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped. with(someHeap) { swap(1, 2); // swap indexes 1 and 2 } -Steve |
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On 5/9/13 8:08 PM, Manu wrote: > On 10 May 2013 09:01, Rob T <alanb@ucora.com <mailto:alanb@ucora.com>> > wrote: > It IS confusing that auto-ref would do 2 completely different things. > One automatically selecting ref-ness, the other saying "i can safely > receive a temporary". There is nothing 'automatic' about the latter. The behaviors are strongly related because automatically selecting refness entails dealing between rvalues and lvalues. The most confusing thing would be to choose between: - ref - auto ref - scope ref It is clear to me things could be designed that way. Practically it would be a massive fail because the last two are so closely related, and different in ever subtle ways. I could safely say on Walter and my behalf that we believe such a design would not serve D well at all. > As I've had to re-iterate countless times, and such is the massive > fallacy behind all of these threads, this whole debate is NOT about > lvalues/rvalues, and I wish people would stop using the term 'rvalue' in > their posts, I worry that they misunderstand the problem every time it's > said. It never hurt any of us to entertain the idea that the fallacy may lie within. > This code is broken: > void f(ref int x) {} > int x; > f(x); That code is not broken. > x is an lvalue. > This is the real problem case, and addressing this will solve the rvalue > case at the same time. > Passing an rvalue to a function just generates an implicit temp which is > functionally identical to the above, except the lifetime of a temp is > usually the life of the statement rather than the outer scope. > The problem we need to solve is that of a function being able to safely > receive a _temporary_. Oh, you mean an rvalue? :o) Andrei |
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, May 09, 2013 21:56:53 Steven Schveighoffer wrote:
> What about specifying ref at the call site when you know you want the data modified?
>
> fix(ref c[i]);
>
> Then if c decides to start returning by value, this is a compiler error.
>
> IMO, fix really should take a pointer. But we want to avoid pointers due to the danger of them.
>
> so this is like applying & but keeps it safe.
That would be great except for UFCS. How would you designate the ref when it's the first argument? And I think that it's worse to have ref optional at the callsite than to not have it at all.
If it weren't for UFCS, I probably would be in favor of requiring it at the callsite, but I just don't see how that could work with UFCS. Maybe C# has a way to deal with that, since it does have some sort of UFCS, and it does require ref at the callsite (at least from what I understand - I haven't used C# much)?
- Jonathan M Davis
|
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On 5/9/13 8:18 PM, Manu wrote:
> Except that auto ref as originally intended seems to have been a flawed
> design, as evidenced by the massive waves this issue keeps creating.
This is news to me. What is the flaw?
Andrei
|
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Thu, 09 May 2013 19:51:47 -0400, Manu <turkeyman@gmail.com> wrote: > On 10 May 2013 08:50, Timon Gehr <timon.gehr@gmx.ch> wrote: >> auto was carried over from C and originally stands for local lifetime. It >> does _not_ mean "apply type deduction here". > > > Eh? 'local lifetime' doesn't sound like it has anything to do with 'apply > type deduction here' to me; which is what D does. auto does not imply type deduction, it is a storage class. D is able to imply type deduction when it knows you are declaring a variable (hence the storage class) and you omit the type. auto actually means 'local' in C. D carries on that tradition, but adds the ability to define the type based on the assignment. These all work: auto x = 1; static x = 1; const x = 1; If the ref storage class could be used in a function/struct, this would work too: ref x = foo(); // assuming foo returns by ref > Is this an argument to continue that trend? > That said, I don't find this to be particularly true. Most things make > reasonable sense. I think there is no good reason to use auto ref, except that it's already in the book. Any storage class would be fine, and auto ref is going to be super-confusing because it's used elsewhere. Just my opinion. -Steve |
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 5/9/13 10:05 PM, Steven Schveighoffer wrote:
> On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 5/9/13 4:36 PM, Peter Alexander wrote:
>>> I'm not sure about how common it is, but I really don't like the idea of
>>> calls like swap(1, 2) being legal. Seems like a step backward from C++.
>>
>> I think if we ever get swap(1, 2) to compile and run we'd effectively
>> have destroyed the D programming language.
>
> Depends on context.
>
> int swap(int diskNum, int partitionNum);
>
> Someone pointed out that swap could be a function that swaps heap
> indexes, and might even take by ref not caring if you want the resulting
> value that was swapped.
>
> with(someHeap)
> {
> swap(1, 2); // swap indexes 1 and 2
> }
>
> -Steve
Now that's great trolling!
Andrei
|
May 10, 2013 Re: The liabilities of binding rvalues to ref | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu Attachments:
| On Thu, May 9, 2013 at 10:14 PM, Andrei Alexandrescu < SeeWebsiteForEmail@erdani.org> wrote:
> On 5/9/13 10:05 PM, Steven Schveighoffer wrote:
>
>> On Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org**> wrote:
>>
>> On 5/9/13 4:36 PM, Peter Alexander wrote:
>>>
>>>> I'm not sure about how common it is, but I really don't like the idea of calls like swap(1, 2) being legal. Seems like a step backward from C++.
>>>>
>>>
>>> I think if we ever get swap(1, 2) to compile and run we'd effectively have destroyed the D programming language.
>>>
>>
>> Depends on context.
>>
>> int swap(int diskNum, int partitionNum);
>>
>> Someone pointed out that swap could be a function that swaps heap indexes, and might even take by ref not caring if you want the resulting value that was swapped.
>>
>> with(someHeap)
>> {
>> swap(1, 2); // swap indexes 1 and 2
>> }
>>
>> -Steve
>>
>
> Now that's great trolling!
>
To clear my name, just in case: I wasn't trolling. I have a use case (in a
heap implementation):
void indexSwap(ref int a, ref int b) {
swap(array[a], array[b]);
swap(a, b);
}
It would be great to be able to call indexSwap(index, index*2 + 1) in one line without having to create a named temporary for the second argument, and with having it mutate index to be index*2 + 1. I think auto ref solves it, though a call-site solution (an inline way to create that temporary) seems like it would work too.
Dmitry
|
Copyright © 1999-2021 by the D Language Foundation