digitalmars.D - The liabilities of binding rvalues to ref
- Andrei Alexandrescu (52/52) May 04 2013 Here are the issues that need to be addressed by any solution
- Walter Bright (5/5) May 04 2013 One solution poffered was making rvalues only bindable to const ref. And...
- Jonathan M Davis (13/17) May 05 2013 Another case is when you want to distinguish between lvalues and rvalues...
- Andrei Alexandrescu (6/12) May 05 2013 Good one. This is in fact what happened in C++: const T& binds so
- Peter Alexander (23/23) May 05 2013 Is there any intention to address the issue of the lvalue-ness of
- Timon Gehr (21/27) May 05 2013 Any solution that allows both lvalues and rvalues to bind to the same
- Timon Gehr (6/34) May 05 2013 It seems to make sense that template function instantiations should be
- Peter Alexander (6/11) May 05 2013 This is not the case currently for template auto ref functions.
- Timon Gehr (4/14) May 05 2013 Very good point about the local static variables. This would be a wart
- Timon Gehr (2/8) May 05 2013 It also would need to be an error to have local static variables.
- Andrei Alexandrescu (9/21) May 05 2013 I think the same. For non-templates "auto ref" should simply accept
- deadalnix (6/11) May 05 2013 Yes ! With optional parenthesis, it is super unclear if we access
- deadalnix (27/27) May 05 2013 I wish to add to the discussion that you want to pass by ref for
- Dicebot (7/15) May 05 2013 You may want to modify rvalue temporary for implementing some
- Steven Schveighoffer (17/22) May 06 2013 ...
- Andrei Alexandrescu (5/19) May 06 2013 Yah, so that's why I'm thinking "auto ref" would fit the bill there.
- Manu (23/48) May 08 2013 ... I feel like this discussion has jumped about 1 week back in time :/
- Peter Alexander (18/25) May 09 2013 The call will not still work fine in C++.
- Manu (5/27) May 09 2013 Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in...
- Steven Schveighoffer (7/10) May 09 2013 I have no problem with ref const accepting rvalues. As I understand it,...
- Peter Alexander (8/14) May 09 2013 Binding rvalues to ref is also dangerous, because the lifetime of
- Steven Schveighoffer (9/21) May 09 2013 I agree, as long as it doesn't make it insanely difficult (i.e. current ...
- Namespace (2/10) May 09 2013 We have been looking for a solution since years. ;)
- Peter Alexander (7/12) May 09 2013 Allowing plain-old ref to bind to rvalues would be a massive
- Steven Schveighoffer (24/34) May 09 2013 The case for it is operator overloads. Note that all operator overloads...
- Jonathan M Davis (18/24) May 09 2013 We already have it with templated functions. For the most part, there wo...
- Peter Alexander (10/26) May 09 2013 C++ has this "hack" as well. Is it really a hack, or just part of
- Steven Schveighoffer (16/40) May 09 2013 I should restate: The idea that restricting rvalues from binding to refs...
- Jonathan M Davis (15/18) May 09 2013 The problem is that while it's obvious in the swap case, it's not always...
- Steven Schveighoffer (11/38) May 09 2013 I understand the point, but it's not as bad as you think. Usually you c...
- Peter Alexander (10/18) May 09 2013 Ah ok. I do agree that the asymmetry is quite hacky, although
- Steven Schveighoffer (9/27) May 09 2013 It's exactly the same actually. 'this' is an implicit ref argument. I ...
- Dmitry S (15/17) May 09 2013 There are useful analogs, however. E.g. consider
- Andrei Alexandrescu (4/6) May 09 2013 I think if we ever get swap(1, 2) to compile and run we'd effectively
- Steven Schveighoffer (12/17) May 09 2013 Depends on context.
- Andrei Alexandrescu (3/21) May 09 2013 Now that's great trolling!
- Dmitry S (14/44) May 09 2013 To clear my name, just in case: I wasn't trolling. I have a use case (in...
- Andrei Alexandrescu (3/4) May 09 2013 And to clear mine: I was referring to std.swap, which swaps two values.
- Steven Schveighoffer (6/34) May 09 2013 That's not great trolling, it's *clever* trolling ;)
- Jonathan M Davis (22/25) May 09 2013 That's trivial enough. All you have to do is lower code like
- Rob T (5/40) May 09 2013 So, if I understand correctly, auto ref for templates will end up
- Andrei Alexandrescu (4/7) May 09 2013 There would be clear restrictions on non-template functions, e.g. a
- Rob T (12/21) May 09 2013 OK I can understand that auto ref on return for a template
- Manu (12/31) May 09 2013 It doesn't make sense though. You're creating an arbitrary restriction
- Rob T (31/41) May 09 2013 Yes, the non-template version cannot ref return an auto ref
- Manu (27/66) May 09 2013 It IS confusing that auto-ref would do 2 completely different things. On...
- Andrei Alexandrescu (16/38) May 09 2013 The behaviors are strongly related because automatically selecting
- Peter Alexander (4/20) May 10 2013 There's nothing broken about that code. In what way do you
- Jonathan M Davis (25/49) May 09 2013 The runtime check for ref that we agreed on already solves the @safety
- Manu (6/68) May 09 2013 What were the arguments again against ref const()? You're talking about
- Steven Schveighoffer (36/41) May 09 2013 the counter-argument goes something like this:
- Andrei Alexandrescu (4/5) May 09 2013 auto x = 5;
- Timon Gehr (2/9) May 09 2013 Returning auto ref is fine as long as no auto ref argument is returned.
- Jonathan M Davis (26/30) May 09 2013 auto ref on templates would be essentially unchanged from what it is now...
- Timon Gehr (4/34) May 09 2013 Actually, no. This would instantiate the ref version in any case, hence
- Manu (20/68) May 09 2013 I don't think this is entirely true, auto ref is a template concept, tha...
- Timon Gehr (8/29) May 09 2013 auto was carried over from C and originally stands for local lifetime.
- Manu (12/40) May 09 2013 It should stay that way. It's reasonable what it does in it's current
- Steven Schveighoffer (17/26) May 09 2013 auto does not imply type deduction, it is a storage class. D is able to...
- Jonathan M Davis (34/49) May 09 2013 I don't buy this at all. The entire point of auto ref on parameters was ...
- Andrei Alexandrescu (6/20) May 09 2013 Exactly. I'll add that just saying "auto is a template concept" is
- Manu (61/119) May 09 2013 Or perhaps it's actually the core promise that was just poorly understoo...
- Timon Gehr (12/58) May 09 2013 No. scope is supposed to restrict escaping (in some way that is still to...
- Manu (19/92) May 09 2013 Fine. @safe is expected to place more restrictions. But it's still
- Rob T (9/24) May 09 2013 However despite the elusiveness of a solution, it looks like
- Manu (10/31) May 09 2013 Except that auto ref as originally intended seems to have been a flawed
- Andrei Alexandrescu (3/5) May 09 2013 This is news to me. What is the flaw?
- Dmitry Olshansky (11/33) May 10 2013 Simply put it wasn't ever implemented like it was meant to.
- Jonathan M Davis (4/14) May 09 2013 Given how restrictive const is in D, I think that it would be a mistake ...
- Jonathan M Davis (28/33) May 09 2013 Not being able to differentiate between when it's an rvalue and lvalue c...
- Steven Schveighoffer (10/29) May 09 2013 What about specifying ref at the call site when you know you want the da...
- Jonathan M Davis (10/21) May 09 2013 That would be great except for UFCS. How would you designate the ref whe...
- Steven Schveighoffer (19/45) May 09 2013 1. using UFCS is optional. c[i].fix() works as well as fix(c[i]).
- Jonathan M Davis (26/53) May 09 2013 That would be an option, but as I explain belong, I think that having re...
- deadalnix (6/32) May 09 2013 It make sense when you pass by ref to modify things. In this case
- Jonathan M Davis (15/50) May 09 2013 In some cases you don't care, but ranges are almost always passed by val...
- Dicebot (3/4) May 10 2013 Ugh, it is usually other way around, at least given current
- Steven Schveighoffer (16/47) May 10 2013 I misunderstood. You would not need ref at callsite because it's
Here are the issues that need to be addressed by any solution reconciling rvalues and passing into functions. This post is not arguing for any particular solution or approach, but lays down the issues that any approach must be judged by. 1. The LRL (Lvalue-Rvalue-Lvalue) problem. This has been long mentioned as an argument in defining C++'s references. The crux of the issue is that caller code passes an lvalue, and the callee code receives an lvalue, but in the middle there's an rvalue created by an implicit conversion. Consider: void fix(ref double x) { if (isnan(x)) x = 0; } ... float a; ... fix(a); If rvalues bind indiscriminately to ref, then the call is legal because of the implicit conversion float->double. A possible solution is to disallow binding if the initial value bound is an lvalue. 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. Changing return types from ref T to T or back and expecting no ill effects (aside from fixing compile-time errors) is a frequent operation in C++ projects I'm involved in. Doing worse than that would be arguably a language design regression. Note that in function call chains fun(gun(hun())), which are common (written as fun.gun.hun etc) in the increasingly popular pipeline-style of defining processing, one function changing return style poisons the well for everybody down the pipeline. That may lead to pipelines that have only partial effect. ======= There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns. Andrei
May 04 2013
One solution poffered was making rvalues only bindable to const ref. Andrei's position was that this prevented many important use cases. The trouble was that const is transitive, and yet what was desired here was so-called "head const". D doesn't have a concept of head const. (C++ const is always "head const".)
May 04 2013
On Sunday, May 05, 2013 01:49:42 Andrei Alexandrescu wrote:There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.Another case is when you want to distinguish between lvalues and rvalues. In fact, IIRC this came up in Ali's dconf talk. He had an opAssign which swapped guts with its argument when it was an rvalue (since it was known to be a temporary) and did a more normal assignment when it was an lvalue. You might still be able to do that if ref accepts rvalues (because the non-ref overload would be preferred in the rvalue case, and the ref overload would be preferred in the lvalue case), but I suspect that that would be incredibly error-prone - especially when there are multiple arguments to the function. So, whatever solution we go with needs to allow us to reasonably overload on refness when we want to while still being able to have functions which accept both lvalues and rvalues by reference. - Jonathan M Davis
May 05 2013
On 5/5/13 3:00 AM, Jonathan M Davis wrote:On Sunday, May 05, 2013 01:49:42 Andrei Alexandrescu wrote:Good one. This is in fact what happened in C++: const T& binds so tightly to T rvalues, that it was impossible to work a wedge in between to allow overloading on T and on const T&. Therefore, a much heavier solution was needed. AndreiThere may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.Another case is when you want to distinguish between lvalues and rvalues.
May 05 2013
Is there any intention to address the issue of the lvalue-ness of "this"? In C++, *this is always an lvalue, even if the member function was called with an rvalue, leading to situations like this: struct Number { void fix() { if (isnan(x)) x = 0; } double x; } class Collection(T) { ref T opIndex(size_t i) { ... } ... } void fixAll(Collection!Number c) { foreach (i; 0 .. c.length) { c[i].fix(); } } Here, if Collection changes to return by non-ref then the fix() call is still valid, silently doing nothing of value. Analogous code in C++ is allowed as well. Do we intend to fix this as well? I suspect there are use cases where such calls are useful, but I can't think of any right now.
May 05 2013
On 05/05/2013 07:49 AM, Andrei Alexandrescu wrote:... There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns. ...Any solution that allows both lvalues and rvalues to bind to the same reference will have problem (2), and fixing up problem (1) introduces somewhat arbitrary rules. Probably we do not want to have to deal with them by default. A possibility would be to fix (2) in a similar way to the proposed solution for (1): Only allow rvalues to bind to 'ref' if they cannot be turned into lvalues by changing the signature of a different function. I don't like this solution very much. I still think auto ref should be extended to work for non-templated functions. The semantics should be the same (i.e. behave as if there were two copies that are eagerly semantically analyzed, but only generate code for one) where possible. It would be an error to have different behaviour of the two "copies". This would mean that in non-templated functions, it should be an error to rely on whether or not the passed argument was an lvalue. i.e. (auto) ref return of an auto ref argument (at least in safe code) and __traits(isRef,autoRefArgument) should be disallowed. This would have the effect of making it illegal to return references to temporaries from functions (at least in safe code), and hence the life time of rvalues would not have to be changed. Both (1) and (2) would be close to non-issues with this solution.
May 05 2013
On 05/05/2013 01:55 PM, Timon Gehr wrote:On 05/05/2013 07:49 AM, Andrei Alexandrescu wrote:It seems to make sense that template function instantiations should be split into two copies per auto ref argument only if it is necessary to support differing semantics for lvalues and rvalues. Peter Alexander brings up a good point regarding the implicit this reference for structs. Probably it should be passed by auto ref.... There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns. ...Any solution that allows both lvalues and rvalues to bind to the same reference will have problem (2), and fixing up problem (1) introduces somewhat arbitrary rules. Probably we do not want to have to deal with them by default. A possibility would be to fix (2) in a similar way to the proposed solution for (1): Only allow rvalues to bind to 'ref' if they cannot be turned into lvalues by changing the signature of a different function. I don't like this solution very much. I still think auto ref should be extended to work for non-templated functions. The semantics should be the same (i.e. behave as if there were two copies that are eagerly semantically analyzed, but only generate code for one) where possible. It would be an error to have different behaviour of the two "copies". This would mean that in non-templated functions, it should be an error to rely on whether or not the passed argument was an lvalue. i.e. (auto) ref return of an auto ref argument (at least in safe code) and __traits(isRef,autoRefArgument) should be disallowed. This would have the effect of making it illegal to return references to temporaries from functions (at least in safe code), and hence the life time of rvalues would not have to be changed. Both (1) and (2) would be close to non-issues with this solution.
May 05 2013
On Sunday, 5 May 2013 at 11:55:28 UTC, Timon Gehr wrote:I still think auto ref should be extended to work for non-templated functions. The semantics should be the same (i.e. behave as if there were two copies that are eagerly semantically analyzed, but only generate code for one) where possible.This is not the case currently for template auto ref functions. For example, the two "versions" of the auto ref function do not share local static variables, nor do they have the same function address. They are literally two (or four, or eight, ...) separate functions. I really dislike how they have been implemented.
May 05 2013
On 05/05/2013 02:11 PM, Peter Alexander wrote:On Sunday, 5 May 2013 at 11:55:28 UTC, Timon Gehr wrote:Very good point about the local static variables. This would be a wart of my proposed solution, as they'd need to be banned for auto ref functions.I still think auto ref should be extended to work for non-templated functions. The semantics should be the same (i.e. behave as if there were two copies that are eagerly semantically analyzed, but only generate code for one) where possible.This is not the case currently for template auto ref functions. For example, the two "versions" of the auto ref function do not share local static variables, nor do they have the same function address.They are literally two (or four, or eight, ...) separate functions. I really dislike how they have been implemented.Me too, but it makes sense to have two functions in some cases.
May 05 2013
On 05/05/2013 01:55 PM, Timon Gehr wrote:...It would be an error to have different behaviour of the two "copies". This would mean that in non-templated functions, it should be an error to rely on whether or not the passed argument was an lvalue. i.e. (auto) ref return of an auto ref argument (at least in safe code) and __traits(isRef,autoRefArgument) should be disallowed. ...It also would need to be an error to have local static variables.
May 05 2013
On 5/5/13 7:55 AM, Timon Gehr wrote:I still think auto ref should be extended to work for non-templated functions. The semantics should be the same (i.e. behave as if there were two copies that are eagerly semantically analyzed, but only generate code for one) where possible. It would be an error to have different behaviour of the two "copies". This would mean that in non-templated functions, it should be an error to rely on whether or not the passed argument was an lvalue. i.e. (auto) ref return of an auto ref argument (at least in safe code) and __traits(isRef,autoRefArgument) should be disallowed. This would have the effect of making it illegal to return references to temporaries from functions (at least in safe code), and hence the life time of rvalues would not have to be changed. Both (1) and (2) would be close to non-issues with this solution.I think the same. For non-templates "auto ref" should simply accept rvalues and lvalues alike. Inside the function an "auto ref" parameter is handled as a local (i.e. disappears at the end of the function) so the compiler disallows returning it by reference directly with the current algorithms. For more complicated patterns, dynamic checking will pick the slack. I'll think I'll rework my DIP for that. Andrei
May 05 2013
On Sunday, 5 May 2013 at 05:49:42 UTC, Andrei Alexandrescu wrote:There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.Yes ! With optional parenthesis, it is super unclear if we access an lvalue or an rvalue and update its content or not. A good example of that is the captures property from std.regex. The property is an revalue, and would look like a filed that reset itself all the time if bound to an lvalue.
May 05 2013
I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense. - You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense. Note that knowing when to pass by value or by reference for performance is more tricky than many people tend to think. The reason is simple : you win an indirection, so if the data are small enough, and especially is the ABI say they are passed via registers, this is a win to pass by value. Many C++ dev will tell you that this is faster, but the fact is that C++ compiler can transform pass by ref into pass by value when it speedup things. It is especially hard problem to solve in generic code. Here is to state the problem. Now let's discuss solutions. As shown before the problem is about performance? And when it come to performance, the first thing we should look for is to allow the optimizer to kick in. The first change I'd propose is to allow the compiler to optimize away all pure copy operation (which include memory allocation). I don't know how far we can go with that, but that is IMO the first direction we should explore. It is simpler for the dev, less error prone, will improve all programs, and do not require any new feature. I'm well aware that the famous sufficiently smart compiler may lurk here, and so we may need to come back on the issue later if that approach fail. It is still worth trying.
May 05 2013
Few additional points: On Sunday, 5 May 2013 at 16:07:12 UTC, deadalnix wrote:I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense.You may want to modify rvalue temporary for implementing some sort of move semantics like in C++11.- You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense.Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object.
May 05 2013
On Sunday, 5 May 2013 at 16:14:01 UTC, Dicebot wrote:Few additional points: On Sunday, 5 May 2013 at 16:07:12 UTC, deadalnix wrote:No, you won't be able to reach any of this anyway. Move semantic is already cleanly implemented in D, so the C++ mess is irrelevant here.I wish to add to the discussion that you want to pass by ref for 2 reasons (and the intent is very different) : - You intend to modify the value in some meaningful way for the caller. In which case, binding to rvalue don't make any sense.You may want to modify rvalue temporary for implementing some sort of move semantics like in C++11.Having a singleton as a value type, and not marking the postblit as disable is asking for trouble in general, nothing specific to the discussion here.- You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense.Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object.
May 05 2013
On Sunday, 5 May 2013 at 16:21:37 UTC, deadalnix wrote:No, you won't be able to reach any of this anyway. Move semantic is already cleanly implemented in D, so the C++ mess is irrelevant here.Agreed.Yes, sure, but you will still use ref if you want ( don't ask why :) ) to use it as an argument to a function and not because of performance but because it is only option. I was objecting to stating that "avoiding copies" -> "performance reasons".Having a singleton as a value type, and not marking the postblit as disable is asking for trouble in general, nothing specific to the discussion here.- You want to avoid creating copies, when this isn't necessary. In this case, this is a performance reason, and binding to rvalue make sense.Not necessarily performance reason on its own, copy construction of given object may have an undesired side effects. Or it may be even some singleton'ish object.
May 05 2013
On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:void fix(ref double x) { if (isnan(x)) x = 0; }...There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.But there are reasons to bind rvalues to references. I think deadalnix said it best, when you want to use ref to modify values, generally you want rvalues to be rejected (though I think rvalue is not a good description, consider that a pointer is an rvalue, but what it points to is an lvalue). When you want to use ref to avoid expensive copies, you want rvalues to be accepted. So I agree with your solution to 1. For 2, I think it's not so simple. A possible solution is to make fix accept a pointer, which is an explicit reference and would not bind to rvalues. In safe code, a pointer could be allowed, just pointer arithmetic disallowed. In addition, Peter's case makes this even more disruptive. 'this' must accept rvalues, so it's not possible to eliminate the problem by preventing binding. -Steve
May 06 2013
On 5/6/13 12:56 PM, Steven Schveighoffer wrote:On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Yah, so that's why I'm thinking "auto ref" would fit the bill there. Requiring pointers for lvalue binding and binding loosely to ref seems like the wrong move. Andreivoid fix(ref double x) { if (isnan(x)) x = 0; }...There may be other important patterns to address at the core, please chime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.But there are reasons to bind rvalues to references. I think deadalnix said it best, when you want to use ref to modify values, generally you want rvalues to be rejected (though I think rvalue is not a good description, consider that a pointer is an rvalue, but what it points to is an lvalue). When you want to use ref to avoid expensive copies, you want rvalues to be accepted.
May 06 2013
On 7 May 2013 00:26, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>wrote:On 5/6/13 12:56 PM, Steven Schveighoffer wrote:... I feel like this discussion has jumped about 1 week back in time :/ In 1) "If rvalues bind indiscriminately to ref, then the call is legal because of the implicit conversion float->double" Implicit conversion should never be allowed when receiving by ref, just like it's an error in C++. There should never be a case where, having passed an lvalue, the function receives a magic temp from an implicit conversion. C++ correctly doesn't allow this, but lots of people here seem to think it does... In 2) "Changing return types from ref T to T or back and expecting no ill effects (aside from fixing compile-time errors) is a frequent operation in C++ projects I'm involved in. Doing worse than that would be arguably a language design regression." Can you show where you would ever make such a change in C++? What do you mean by 'worse'? The behaviour is to be expected... if opIndex doesn't return a ref, why would you expect that the caller can possibly change it? I don't see any problem here... what 'ill effects' are you referring to? Perhaps you're arguing that the problem is that the user _isn't_ getting compiler complaints when the code is changed? The call that modifies it will still work fine, but it will obviously apply the changes to a temp that is then lost? Surely this is to be expected?On Sat, 04 May 2013 22:49:42 -0700, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org**> wrote: void fix(ref double x) { if (isnan(x)) x = 0; }Yah, so that's why I'm thinking "auto ref" would fit the bill there. Requiring pointers for lvalue binding and binding loosely to ref seems like the wrong move.... There may be other important patterns to address at the core, pleasechime in. I consider (1) above easy to tackle, which leaves us with at least (2). My opinion is that any proposal for binding rvalues to ref must offer a compelling story about these patterns.But there are reasons to bind rvalues to references. I think deadalnix said it best, when you want to use ref to modify values, generally you want rvalues to be rejected (though I think rvalue is not a good description, consider that a pointer is an rvalue, but what it points to is an lvalue). When you want to use ref to avoid expensive copies, you want rvalues to be accepted.
May 08 2013
On Thursday, 9 May 2013 at 04:00:55 UTC, Manu wrote:Perhaps you're arguing that the problem is that the user _isn't_ getting compiler complaints when the code is changed? The call that modifies it will still work fine, but it will obviously apply the changes to a temp that is then lost? Surely this is to be expected?The call will not still work fine in C++. Here's the code again: 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]); } } In (analogous) C++, if Collection's opIndex changes to return by value then the call to fix is a compile time error (the rvalues don't bind to unqualified ref). I believe Andrei is arguing that D must _at least_ do this to make progress, i.e. not be "worse" (than C++).
May 09 2013
On 9 May 2013 17:26, Peter Alexander <peter.alexander.au gmail.com> wrote:On Thursday, 9 May 2013 at 04:00:55 UTC, Manu wrote:Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in D that 'ref const()' is too restrictive. We're about 1 post away from finding ourselves arguing over 'scope ref'/'auto ref' again...Perhaps you're arguing that the problem is that the user _isn't_ getting compiler complaints when the code is changed? The call that modifies it will still work fine, but it will obviously apply the changes to a temp that is then lost? Surely this is to be expected?The call will not still work fine in C++. Here's the code again: 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]); } } In (analogous) C++, if Collection's opIndex changes to return by value then the call to fix is a compile time error (the rvalues don't bind to unqualified ref). I believe Andrei is arguing that D must _at least_ do this to make progress, i.e. not be "worse" (than C++).
May 09 2013
On Thu, 09 May 2013 04:20:02 -0400, Manu <turkeyman gmail.com> wrote:Ah yes, right, C++ fix() would need to be 'const ref&', but we decide in D that 'ref const()' is too restrictive.I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D. -Steve
May 09 2013
On Thursday, 9 May 2013 at 13:00:44 UTC, Steven Schveighoffer wrote:I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.Binding rvalues to ref is also dangerous, because the lifetime of the rvalue is separate from that of the ref, and the ref has no way of knowing when the rvalue will be destroyed. The ref safety proposal will make it a runtime error to return a reference to a destroyed object, but it would be better if we reduced the opportunities for runtime errors if possible.
May 09 2013
On Thu, 09 May 2013 12:51:04 -0400, Peter Alexander <peter.alexander.au gmail.com> wrote:On Thursday, 9 May 2013 at 13:00:44 UTC, Steven Schveighoffer wrote:I agree, as long as it doesn't make it insanely difficult (i.e. current situation) to make types that deal with rvalues. Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too. -SteveI have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.Binding rvalues to ref is also dangerous, because the lifetime of the rvalue is separate from that of the ref, and the ref has no way of knowing when the rvalue will be destroyed. The ref safety proposal will make it a runtime error to return a reference to a destroyed object, but it would be better if we reduced the opportunities for runtime errors if possible.
May 09 2013
I agree, as long as it doesn't make it insanely difficult (i.e. current situation) to make types that deal with rvalues. Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too. -SteveWe have been looking for a solution since years. ;) It will take a very long long time. That is certain.
May 09 2013
On Thursday, 9 May 2013 at 17:27:41 UTC, Steven Schveighoffer wrote:Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too.Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post. It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).
May 09 2013
On Thu, 09 May 2013 13:45:16 -0400, Peter Alexander <peter.alexander.au gmail.com> wrote:On Thursday, 9 May 2013 at 17:27:41 UTC, Steven Schveighoffer wrote:The case for it is operator overloads. Note that all operator overloads are defined WITHIN types, and all operator overloads take 'this' by reference. Without the hack currently in the compiler that 'this' is allowed to bind by ref to rvalues, D would be near useless. Consider that today, this doesn't work for custom structs, unless you allocate the result on the heap. ((a + b) + (c + d)) The original case 2 looks bad, but not very common. With by-ref turning into by-value, you are in effect changing the API and functionality. It would just mean that you wouldn't make that change, or change the type name at the same time (so it wouldn't just compile on existing code). I think someone later mentioned that if 'fix' was a method, it would break even today (with ref-binds-to-this hack). You should also consider that even if we break existing code by changing by-ref into by-value, there's no guarantee the person maintaining the code does the right thing.Short answer: no matter what, we have to find a way to be able to write one function that takes by ref, and accepts both rvalues and lvalues. If ref const is that way, so be it. If auto ref is that way, so be it. If it's plain-old ref, I'm fine with that too.Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post.It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).I think first we have to prove that auto ref is worth the extra cognitive load. Every overload style we add has a large multiplicative effect on boilerplate. As I said before, I'd be ok with any solution, as long as we can bind rvalues to some form of ref. -Steve
May 09 2013
On Thursday, May 09, 2013 14:23:11 Steven Schveighoffer wrote:I think first we have to prove that auto ref is worth the extra cognitive load. Every overload style we add has a large multiplicative effect on boilerplate. As I said before, I'd be ok with any solution, as long as we can bind rvalues to some form of ref.We already have it with templated functions. For the most part, there would be no real difference from the caller's perspective between auto ref with a templated function and auto ref with a non-templated functions. You just use auto ref (or auto ref const if you can use const) whenever you want a parameter to accept both rvalues and lvalues, and whether the function is templated or not doesn't really matter. The only times that it would matter are when you actually care about forwarding the refness of the argument (which the templated functon will do but not the non-templated one) or when you don't want the extra template bloat of the templated auto ref (though it might be possible for the compiler to use the non-templated solution for auto ref when it can determine that ref forwarding isn't being used). Using auto ref in this manner is exactly what it was originally intended for and exactly what TDPL describes. So, I don't think that the difference between templated auto ref and non-templated auto ref will add much cognitive load, and we've already added the cognitive load of having auto ref by having it with templated functions. - Jonathan M Davis
May 09 2013
On Thursday, 9 May 2013 at 18:23:12 UTC, Steven Schveighoffer wrote:On Thu, 09 May 2013 13:45:16 -0400, Peter AlexanderC++ has this "hack" as well. Is it really a hack, or just part of the language design?Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post.The case for it is operator overloads. Note that all operator overloads are defined WITHIN types, and all operator overloads take 'this' by reference. Without the hack currently in the compiler that 'this' is allowed to bind by ref to rvalues, D would be near useless.Consider that today, this doesn't work for custom structs, unless you allocate the result on the heap. ((a + b) + (c + d))I don't follow. You say "today", but this works for me in latest dmd. Do you mean hypothetically without the "hack", or am I misunderstanding?The original case 2 looks bad, but not very common. With by-ref turning into by-value, you are in effect changing the API and functionality. It would just mean that you wouldn't make that change, or change the type name at the same time (so it wouldn't just compile on existing code).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++.
May 09 2013
On Thu, 09 May 2013 16:36:27 -0400, Peter Alexander <peter.alexander.au gmail.com> wrote:On Thursday, 9 May 2013 at 18:23:12 UTC, Steven Schveighoffer wrote:I should restate: The idea that restricting rvalues from binding to refs *except* the case where it binds to 'this' is a hack. The binding to 'this' pre-dates the restriction.On Thu, 09 May 2013 13:45:16 -0400, Peter AlexanderC++ has this "hack" as well. Is it really a hack, or just part of the language design?Allowing plain-old ref to bind to rvalues would be a massive mistake in my opinion. See case (2) in the original post.The case for it is operator overloads. Note that all operator overloads are defined WITHIN types, and all operator overloads take 'this' by reference. Without the hack currently in the compiler that 'this' is allowed to bind by ref to rvalues, D would be near useless.I mean it doesn't work today if it takes the parameter by ref.Consider that today, this doesn't work for custom structs, unless you allocate the result on the heap. ((a + b) + (c + d))I don't follow. You say "today", but this works for me in latest dmd. Do you mean hypothetically without the "hack", or am I misunderstanding?Why is it so bad that that is legal? Really hard to stop people from writing incorrect code. In your case, it actually LOOKS wrong, so that's not really a case we should be concerned about. The original case 2 is worse. int i = 1; swap(i, i); That's also legal. *(cast(int *)null) = 5; That's also legal. -SteveThe original case 2 looks bad, but not very common. With by-ref turning into by-value, you are in effect changing the API and functionality. It would just mean that you wouldn't make that change, or change the type name at the same time (so it wouldn't just compile on existing code).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++.
May 09 2013
On Thursday, May 09, 2013 16:59:13 Steven Schveighoffer wrote:Why is it so bad that that is legal? Really hard to stop people from writing incorrect code. In your case, it actually LOOKS wrong, so that's not really a case we should be concerned about.The problem is that while it's obvious in the swap case, it's not always obvious. And when you look at a function signature and see that it accepts its argument by ref (and therefore doesn't accept rvalues), you can reasonably assume that it's intended to alter its argument, whereas if it takes by auto ref (which then would accept rvalues), you know that the intention is for the function to accept arguments by ref for efficiency and not for mutation. You do need const to fully guarantee that, but it still gives a definite indicator of intent even without that (and const is too restrictive for some cases to require it). And if ref accepted rvalues, you'd end up with functions using ref as a matter of course for the efficiency gain (as occurs in C++ with const&) and wouldn't have a clue which ones were intended to mutate their arguments and which were just doing it for efficiency (since unlike C++, const wouldn't be required and might even be avoided given how restrictive it is in D). - Jonathan M Davis
May 09 2013
On Thu, 09 May 2013 17:06:43 -0400, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Thursday, May 09, 2013 16:59:13 Steven Schveighoffer wrote:I understand the point, but it's not as bad as you think. Usually you can discern from the function name what its going to do (e.g. the 'fix' function from the example). Having a storage class that accepts by ref and then *might* change them is really kind of useless. If auto ref is used, there should be no promises for that storage class. In any case, I'm fine with auto ref if that is the solution. I just wonder if it's worth the trouble. I suppose we will know the answer only after having it implemented for a while. -SteveWhy is it so bad that that is legal? Really hard to stop people from writing incorrect code. In your case, it actually LOOKS wrong, so that's not really a case we should be concerned about.The problem is that while it's obvious in the swap case, it's not always obvious. And when you look at a function signature and see that it accepts its argument by ref (and therefore doesn't accept rvalues), you can reasonably assume that it's intended to alter its argument, whereas if it takes by auto ref (which then would accept rvalues), you know that the intention is for the function to accept arguments by ref for efficiency and not for mutation. You do need const to fully guarantee that, but it still gives a definite indicator of intent even without that (and const is too restrictive for some cases to require it). And if ref accepted rvalues, you'd end up with functions using ref as a matter of course for the efficiency gain (as occurs in C++ with const&) and wouldn't have a clue which ones were intended to mutate their arguments and which were just doing it for efficiency (since unlike C++, const wouldn't be required and might even be avoided given how restrictive it is in D).
May 09 2013
On Thursday, 9 May 2013 at 20:59:14 UTC, Steven Schveighoffer wrote:I should restate: The idea that restricting rvalues from binding to refs *except* the case where it binds to 'this' is a hack. The binding to 'this' pre-dates the restriction.Ah ok. I do agree that the asymmetry is quite hacky, although binding to 'this' isn't quite the same as binding to ref.You could apply the same argument against static typing, e.g. "swap(apple, orange) looks wrong, so why is it so bad not to catch the type error at compile time?" I think we can agree that while it's not possible to stop people from writing incorrect code 100% of the time, there is still benefit to catching as many cases as practical.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++.Why is it so bad that that is legal? Really hard to stop people from writing incorrect code.
May 09 2013
On Thu, 09 May 2013 17:45:33 -0400, Peter Alexander <peter.alexander.au gmail.com> wrote:On Thursday, 9 May 2013 at 20:59:14 UTC, Steven Schveighoffer wrote:It's exactly the same actually. 'this' is an implicit ref argument. I don't see how it's any different. We also currently have a hack where ++ doesn't work on rvalues artificially, but an .increment() function does.I should restate: The idea that restricting rvalues from binding to refs *except* the case where it binds to 'this' is a hack. The binding to 'this' pre-dates the restriction.Ah ok. I do agree that the asymmetry is quite hacky, although binding to 'this' isn't quite the same as binding to ref.I define practical as catching as many things that shouldn't work as you can without making it impossible to write things that should. -SteveYou could apply the same argument against static typing, e.g. "swap(apple, orange) looks wrong, so why is it so bad not to catch the type error at compile time?" I think we can agree that while it's not possible to stop people from writing incorrect code 100% of the time, there is still benefit to catching as many cases as practical.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++.Why is it so bad that that is legal? Really hard to stop people from writing incorrect code.
May 09 2013
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++.There are useful analogs, however. E.g. consider heap.swap(parentIndex, parentIndex * 2 + 1) heap.swap(getParent(index), index) It's convenient and intuitive if the lvalues in these calls can be accepted by reference and modified. And it's not a problem that the modification to the rvalue is lost -- that's what's desired in this case. One alternative which I haven't seen (though I haven't read the entire thread) is to require the caller to add some syntax when passing an rvalue to a ref parameter. E.g. in the above example heap.swap(parentIndex, tempRef(parentIndex * 2 + 1)) would look a little clearer, by making it more explicit what's going on with that second parameter. I imagine "tempRef" would have to be some language feature, a sort of alternative to "auto ref", but to be used at the call site. Dmitry
May 09 2013
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. Andrei
May 09 2013
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: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 } -SteveI'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.
May 09 2013
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:Now that's great trolling! AndreiOn 5/9/13 4:36 PM, Peter Alexander wrote: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 } -SteveI'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.
May 09 2013
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: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. DmitryOn Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org**> wrote: On 5/9/13 4:36 PM, Peter Alexander wrote:Now that's great trolling!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 } -SteveI'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.
May 09 2013
On 5/9/13 10:23 PM, Dmitry S wrote:To clear my name, just in case: I wasn't trolling.And to clear mine: I was referring to std.swap, which swaps two values. Andrei
May 09 2013
On Thu, 09 May 2013 22:14:33 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 5/9/13 10:05 PM, Steven Schveighoffer wrote:That's not great trolling, it's *clever* trolling ;) I was actually half serious though, the name of the function is really important here, including the context. -SteveOn Thu, 09 May 2013 21:47:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Now that's great trolling!On 5/9/13 4:36 PM, Peter Alexander wrote: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 } -SteveI'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.
May 09 2013
On Thursday, May 09, 2013 19:45:16 Peter Alexander wrote:It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).That's trivial enough. All you have to do is lower code like auto foo(auto ref int i) {...} foo(5); to something like auto foo(ref int i) {...} auto __temp = 5; foo(__temp); And temporaries end up on the stack anyway, so you wouldn't really even have to lower it to quite like that, but that's what would be happening conceptually. It's also what would happen if plain ref accepted rvalues. It's just that we avoid certain classes of issues by having something distinct from plain ref for accepting rvalues by ref. The implementation itself is straightforward. If anything, I think that argument comes down primarily to two things: 1. Should plain ref accept rvlaues? 2. If plain ref shouldn't accept rvalues, then what attribute do we use to accept rvalues by ref? And given that the whole point of adding auto ref to the language was to solve exactly this problem, I think that it makes perfect sense to just use auto ref. - Jonathan M Davis
May 09 2013
On Thursday, 9 May 2013 at 19:26:37 UTC, Jonathan M Davis wrote:On Thursday, May 09, 2013 19:45:16 Peter Alexander wrote:So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).That's trivial enough. All you have to do is lower code like auto foo(auto ref int i) {...} foo(5); to something like auto foo(ref int i) {...} auto __temp = 5; foo(__temp); And temporaries end up on the stack anyway, so you wouldn't really even have to lower it to quite like that, but that's what would be happening conceptually. It's also what would happen if plain ref accepted rvalues. It's just that we avoid certain classes of issues by having something distinct from plain ref for accepting rvalues by ref. The implementation itself is straightforward. If anything, I think that argument comes down primarily to two things: 1. Should plain ref accept rvlaues? 2. If plain ref shouldn't accept rvalues, then what attribute do we use to accept rvalues by ref? And given that the whole point of adding auto ref to the language was to solve exactly this problem, I think that it makes perfect sense to just use auto ref. - Jonathan M Davis
May 09 2013
On 5/9/13 3:39 PM, Rob T wrote:So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
May 09 2013
On Thursday, 9 May 2013 at 19:43:25 UTC, Andrei Alexandrescu wrote:On 5/9/13 3:39 PM, Rob T wrote:OK I can understand that auto ref on return for a template function is something different, so disallowing auto ref on a regular function makes perfect sense to me. You can also create template functions with auto ref params that behave exactly like a non-template counter part by not specifying ref on the return, so it seems consistent and a sound solution from that perspective. D is a complex language, so stuff like this does take some getting used to, but it is very powerful and flexible, no two ways around it.So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
May 09 2013
On 10 May 2013 06:07, Rob T <alanb ucora.com> wrote:On Thursday, 9 May 2013 at 19:43:25 UTC, Andrei Alexandrescu wrote:It doesn't make sense though. You're creating an arbitrary restriction because overloading an existing meaning doesn't apply (actually conflicts) in this case. auto is a template concept, it should not be applied here. Refer to all the reasoning behind scope ref instead... You can also create template functions with auto ref params that behaveOn 5/9/13 3:39 PM, Rob T wrote:OK I can understand that auto ref on return for a template function is something different, so disallowing auto ref on a regular function makes perfect sense to me.So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andreiexactly like a non-template counter part by not specifying ref on the return, so it seems consistent and a sound solution from that perspective.Umm, what if the non-template counterpart returns ref? Then it doesn't behave the same. D is a complex language, so stuff like this does take some getting used to,but it is very powerful and flexible, no two ways around it.If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.
May 09 2013
On Thursday, 9 May 2013 at 21:55:51 UTC, Manu wrote:Umm, what if the non-template counterpart returns ref? Then it doesn't behave the same.Yes, the non-template version cannot ref return an auto ref param, but I would expect that neither can the template version unless auto ref is specified as the return value. The key difference that I think can be understood is that auto ref on the return value for a template is not the same thing as auto ref on the param. For templates, you can enforce exactly the same behavior as the non-template version by specifying a return of either ref or not, ie you do not specify auto ref or you specify a return by value. If you so happen to specify a ref return of an auto ref param, I would expect the compiler to refuse to compile no matter if it is a template function or not. Of course you can confuse the programmer and allow such a thing to compile only for the cases where it can return ref on the auto ref, but IMO that'll be a big mistake as it will confuse the s*** out of most people, and besides allowing something like that has no value at all.D is a complex language, so stuff like this does take some getting used to,Well, I suppose I cannot disagree with you on that point, so yes it is confusing, but for sake of a solution, it is not nearly as confusing so long as auto ref on the parameters will behave the same for both template and non-template versions in a consistent way. I know auto ref on the return is potentially confusing, but it does increase template flexibility as the benefit. Alternatively, I think Jonathan's argument against scope ref makes perfect sense. Unless I misread something or he's dead wrong, how can scope be used without creating even more confusion? Even if scope has some properties in common with auto ref, it specifies something entirely different than accepting rvalues and lvalues. This point reminds me of why we should not be using bool as an integral type, it's the same reasoning. --rtbut it is very powerful and flexible, no two ways around it.If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.
May 09 2013
On 10 May 2013 09:01, Rob T <alanb ucora.com> wrote:On Thursday, 9 May 2013 at 21:55:51 UTC, Manu 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. Alternatively, I think Jonathan's argument against scope ref makes perfectUmm, what if the non-template counterpart returns ref? Then it doesn't behave the same.Yes, the non-template version cannot ref return an auto ref param, but I would expect that neither can the template version unless auto ref is specified as the return value. The key difference that I think can be understood is that auto ref on the return value for a template is not the same thing as auto ref on the param. For templates, you can enforce exactly the same behavior as the non-template version by specifying a return of either ref or not, ie you do not specify auto ref or you specify a return by value. If you so happen to specify a ref return of an auto ref param, I would expect the compiler to refuse to compile no matter if it is a template function or not. Of course you can confuse the programmer and allow such a thing to compile only for the cases where it can return ref on the auto ref, but IMO that'll be a big mistake as it will confuse the s*** out of most people, and besides allowing something like that has no value at all. D is a complex language, so stuff like this does take some getting usedto,Well, I suppose I cannot disagree with you on that point, so yes it is confusing, but for sake of a solution, it is not nearly as confusing so long as auto ref on the parameters will behave the same for both template and non-template versions in a consistent way. I know auto ref on the return is potentially confusing, but it does increase template flexibility as the benefit.but it is very powerful and flexible, no two ways around it.If this takes 'getting used to', you're basically admitting that it doesn't make intuitive sense.sense. Unless I misread something or he's dead wrong, how can scope be used without creating even more confusion? Even if scope has some properties in common with auto ref, it specifies something entirely different than accepting rvalues and lvalues. This point reminds me of why we should not be using bool as an integral type, it's the same reasoning.Which argument? Correct, it specifies something _entirely different_, it says "I can safely receive a temporary, because I promise not to escape it". This is the actual problem that we're trying to solve, and it addresses the problem head on. 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. This code is broken: void f(ref int x) {} int x; f(x); 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_.
May 09 2013
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 09 2013
On Friday, 10 May 2013 at 00:08:50 UTC, Manu wrote: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. This code is broken: void f(ref int x) {} int x; f(x); x is an lvalue.There's nothing broken about that code. In what way do you believe it is broken?The problem we need to solve is that of a function being able to safely receive a _temporary_.temporary = rvalue
May 10 2013
On Friday, May 10, 2013 10:08:37 Manu wrote:Correct, it specifies something _entirely different_, it says "I can safely receive a temporary, because I promise not to escape it". This is the actual problem that we're trying to solve, and it addresses the problem head on. 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. This code is broken: void f(ref int x) {} int x; f(x); 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_.The runtime check for ref that we agreed on already solves the safety problem. So, I see no point in discussing the safety problem further unless there's something wrong with the runtime check solution. And yes, the safety problem is not just a question of rvalues. But whether ref should accept rvalues is very important with regards to being able to write and understand correct and maintainable code. The question of accepting rvalues that we are therefore discussing has _nothing_ to do with safety. It's entirely a question of avoiding other types of bugs - like accepting nonsense like swap(5, 7), which in that case is fortunately obvious but is not obvious in the general case. IMHO, it needs to be clear when a function intends to take an argument by ref because it intends to mutate the argument and when it intends to take an argument by ref because it wants the efficiency boost of avoiding the copy. In the first case, it makes no sense to accept rvalues, and in the second case, you definitely want to accept rvalues. As such, having different syntax is needed (be it auto ref or acceptrvalue or whatever). I'm not entirely against adding a new attribute for that (it would have the added benefit of not needing a compiler optimization to guarantee that a templated function takes its argument by ref when passed an rvalue), but Walter and Andrei don't want to add new attributes if they can avoid it, so I don't expect them to be okay with adding a new attribute. And since auto ref was originally supposed to be this attribute, I'd _much_ rather have that do it than make the mistake of letting ref accept rvalues. - Jonathan M Davis
May 09 2013
On 10 May 2013 10:31, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Friday, May 10, 2013 10:08:37 Manu 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.Correct, it specifies something _entirely different_, it says "I cansafelyreceive a temporary, because I promise not to escape it". This is the actual problem that we're trying to solve, and it addresses the problem head on. As I've had to re-iterate countless times, and such is the massivefallacybehind 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. This code is broken: void f(ref int x) {} int x; f(x); 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_.The runtime check for ref that we agreed on already solves the safety problem. So, I see no point in discussing the safety problem further unless there's something wrong with the runtime check solution. And yes, the safety problem is not just a question of rvalues. But whether ref should accept rvalues is very important with regards to being able to write and understand correct and maintainable code. The question of accepting rvalues that we are therefore discussing has _nothing_ to do with safety. It's entirely a question of avoiding other types of bugs - like accepting nonsense like swap(5, 7), which in that case is fortunately obvious but is not obvious in the general case. IMHO, it needs to be clear when a function intends to take an argument by ref because it intends to mutate the argument and when it intends to take an argument by ref because it wants the efficiency boost of avoiding the copy. In the first case, it makes no sense to accept rvalues, and in the second case, you definitely want to accept rvalues. As such, having different syntax is needed (be it auto ref or acceptrvalue or whatever). I'm not entirely against adding a new attribute for that (it would have the added benefit of not needing a compiler optimization to guarantee that a templated function takes its argument by ref when passed an rvalue), but Walter and Andrei don't want to add new attributes if they can avoid it, so I don't expect them to be okay with adding a new attribute. And since auto ref was originally supposed to be this attribute, I'd _much_ rather have that do it than make the mistake of letting ref accept rvalues.
May 09 2013
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 09 2013
On 5/9/13 5:55 PM, Manu wrote:auto is a template concept, it should not be applied here.auto x = 5; Where's the template? Andrei
May 09 2013
On 05/09/2013 09:43 PM, Andrei Alexandrescu wrote:On 5/9/13 3:39 PM, Rob T wrote:Returning auto ref is fine as long as no auto ref argument is returned.So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.There would be clear restrictions on non-template functions, e.g. a non-template cannot return auto ref. Andrei
May 09 2013
On Thursday, May 09, 2013 21:39:14 Rob T wrote:So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.auto ref on templates would be essentially unchanged from what it is now (the fact that it forwards the refness of its arguments is actually very useful for some situations, and we don't want to lose that). However, what we might be able to do as an optimization would be to make it so that auto ref on templates acts the same as auto ref on non-templated functions when the compiler is able to determine that there's no real semantic difference - i.e. when the fact that the refness is being forwarded is irrelevant. So, with a function like auto foo(T)(auto ref T t) { return t; } the non-templated solution could be used, whereas in a function like auto ref foo(T)(auto ref T t) { return t; } or auto foo(T)(auto ref T t) { return anotherFuncWhichTakesByAutoRef(t); } the compiler would have to use the normal templated solution, because the refness of the arguments would have an effect on the code. - Jonathan M Davis
May 09 2013
On 05/09/2013 11:06 PM, Jonathan M Davis wrote:On Thursday, May 09, 2013 21:39:14 Rob T wrote:Yes.So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.auto ref on templates would be essentially unchanged from what it is now (the fact that it forwards the refness of its arguments is actually very useful for some situations, and we don't want to lose that). However, what we might be able to do as an optimization would be to make it so that auto ref on templates acts the same as auto ref on non-templated functions when the compiler is able to determine that there's no real semantic difference - i.e. when the fact that the refness is being forwarded is irrelevant. So, with a function like auto foo(T)(auto ref T t) { return t; } the non-templated solution could be used, whereas in a function like auto ref foo(T)(auto ref T t) { return t; } orauto foo(T)(auto ref T t) { return anotherFuncWhichTakesByAutoRef(t); } the compiler would have to use the normal templated solution, because the refness of the arguments would have an effect on the code. ...Actually, no. This would instantiate the ref version in any case, hence only one instantiation is necessarily required.
May 09 2013
On 10 May 2013 05:39, Rob T <alanb ucora.com> wrote:On Thursday, 9 May 2013 at 19:26:37 UTC, Jonathan M Davis 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. So to say it will do 'exactly the same thing' is a misunderstanding. I argue that 'auto ref' as applied to non-templates will only create confusion, it effectively re-enforces the type of confusion that you have just shown. This is the reasoning for the argument behind scope ref, which to my mind actually makes good sound sense, and should lead people to a proper understanding of what you are actually doing. Considering the key argument against 'scope ref' is that people don't want to require more attributes to make use of it, I don't see how 'auto ref' satisfies this argument either. Thus, I am quite happy happy with 'ref', it can be made safe, satisfies the argument above, and this seems like a very good start that we might actually all agree on.On Thursday, May 09, 2013 19:45:16 Peter Alexander wrote:So, if I understand correctly, auto ref for templates will end up doing exactly the same thing as auto ref for non-template functions? That would be perfect, otherwise it'll be terribly confusing.It seems that 'auto ref' would be suitable, provided we can find a way for it to work with normal functions (in a sensible way, not like templates).That's trivial enough. All you have to do is lower code like auto foo(auto ref int i) {...} foo(5); to something like auto foo(ref int i) {...} auto __temp = 5; foo(__temp); And temporaries end up on the stack anyway, so you wouldn't really even have to lower it to quite like that, but that's what would be happening conceptually. It's also what would happen if plain ref accepted rvalues. It's just that we avoid certain classes of issues by having something distinct from plain ref for accepting rvalues by ref. The implementation itself is straightforward. If anything, I think that argument comes down primarily to two things: 1. Should plain ref accept rvlaues? 2. If plain ref shouldn't accept rvalues, then what attribute do we use to accept rvalues by ref? And given that the whole point of adding auto ref to the language was to solve exactly this problem, I think that it makes perfect sense to just use auto ref. - Jonathan M Davis
May 09 2013
On 05/09/2013 11:35 PM, Manu wrote:... I don't think this is entirely true, auto ref is a template concept,In the current implementation, but not necessarily.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).auto was carried over from C and originally stands for local lifetime. It does _not_ mean "apply type deduction here".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.Most keywords are poorly chosen.So to say it will do 'exactly the same thing' is a misunderstanding. I argue that 'auto ref' as applied to non-templates will only create confusion, it effectively re-enforces the type of confusion that you have just shown. This is the reasoning for the argument behind scope ref, which to my mind actually makes good sound sense, and should lead people to a proper understanding of what you are actually doing. Considering the key argument against 'scope ref' is that people don't want to require more attributes to make use of it,This is inaccurate.I don't see how 'auto ref' satisfies this argument either.Sure, it wouldn't.Thus, I am quite happy happy with 'ref', it can be made safe, satisfies the argument above, and this seems like a very good start that we might actually all agree on.It can make code evolution less straightforward.
May 09 2013
On 10 May 2013 08:50, Timon Gehr <timon.gehr gmx.ch> wrote:On 05/09/2013 11:35 PM, Manu wrote:It should stay that way. It's reasonable what it does in it's current implementation. that is, "automatic ref-ness", it selects the ref-ness of the argument... I don't think this is entirely true, auto ref is a template concept,In the current implementation, but not necessarily.Eh? 'local lifetime' doesn't sound like it has anything to do with 'apply type deduction here' to me; which is what D does. This concept doesn'tautomatically, at compile time, just like auto applied everywhere else (selects a type for instance, at compile time).auto was carried over from C and originally stands for local lifetime. It does _not_ mean "apply type deduction here".Is this an argument to continue that trend? That said, I don't find this to be particularly true. Most things make reasonable sense. So to say it will do 'exactly the same thing' is a misunderstanding. Imake any sense applied to a non-template. It *IS* a ref as specified by the programmer, there's nothing 'automatic' about it.Most keywords are poorly chosen.It's the most consistent argument against scope-ref.argue that 'auto ref' as applied to non-templates will only create confusion, it effectively re-enforces the type of confusion that you have just shown. This is the reasoning for the argument behind scope ref, which to my mind actually makes good sound sense, and should lead people to a proper understanding of what you are actually doing. Considering the key argument against 'scope ref' is that people don't want to require more attributes to make use of it,This is inaccurate.
May 09 2013
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 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 refauto 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.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 09 2013
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.This is the reasoning for the argument behind scope ref, which to my mind actually makes good sound sense, and should lead people to a proper understanding of what you are actually doing.The whole point of making auto ref work with non-templated functions is to be able to say that you want to pass both rvalues and lvalues by ref so that they get passed efficiently. scope says _nothing_ about that. It's all about what escapes the scope, not about how stuff gets passed. So, using scope ref to solve that problem makes no sense, and the changes that we've proposed to make to ref to make it safe pretty much make scope unnecessary. scope ref would be virtually identical to ref given that ref already has to guarantee that the variable being referenced doesn't get destroyed before the ref is. The only real difference would be that scope would presumably additionally prevent doing system stuff like taking the address of the ref. I don't see how this buys us anything. I agree that auto ref isn't a great name, but it's what we already have, and using it on non-templated functions would be using it for exactly what it was designed for in the first place and how it's described in TDPL. The only reason that it's not what we have now is because Walter misunderstood it when he implemented it.Considering the key argument against 'scope ref' is that people don't want to require more attributes to make use of it, I don't see how 'auto ref' satisfies this argument either.Because we already have auto ref. It just hasn't been implemented on non- templated functions yet even though it was supposed to be. And you were proposing not only using scope ref with a particular meaning on function parameters but also adding it as a return type, which it definitely isn't know regardless of what scope of function parameters does or doesn't do. And it's even questionable that scope as originally intended can be properly implemented anyway.Thus, I am quite happy happy with 'ref', it can be made safe, satisfies the argument above, and this seems like a very good start that we might actually all agree on.As has already been discussed in this thread, it will introduce maintenance problems if ref accepts rvalues. - Jonathan M Davis
May 09 2013
On 5/9/13 6:09 PM, Jonathan M Davis wrote:On Friday, May 10, 2013 07:35:36 Manu wrote: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. AndreiI 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.
May 09 2013
On 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Friday, May 10, 2013 07:35:36 Manu wrote:Or perhaps it's actually the core promise that was just poorly understood at the time? auto ref is already described in TDPL, and it has nothing toI 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.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.Making it blindly suit the book, whether that idea was initially right or wrong without having actually tried it out or argued it through is not a compelling argument for my money.This is the reasoning for the argument behind scope ref, which to my mindEfficiency can not be implied by ref-ness. There are lots of factors, and the programmer needs control over which is chosen. auto ref would seem to me as something of a hack that is required when dealing with ref and templates, because ref is not part of the type like C++. auto ref on templates wouldn't exist if ref was part of the type, it would infer ref-ness automatically like C++ does. Even Walter agreed that it felt like a hack for this reason during conversations. scope says _nothing_ about that. It's all about whatactually makes good sound sense, and should lead people to a proper understanding of what you are actually doing.The whole point of making auto ref work with non-templated functions is to be able to say that you want to pass both rvalues and lvalues by ref so that they get passed efficiently.escapes the scope, not about how stuff gets passed.It says it in a more pure way. The point is that it is made explicit that the argument will not escape the scope it is given, thus, in conjunction with ref, it becomes naturally safe to pass r-values. So, using scope ref tosolve that problem makes no sense, and the changes that we've proposed to make to ref to make it safe pretty much make scope unnecessary.I agree, that's why I'm also happy with 'ref' alone, but I still feel it doesn't communicate as much information, which is a trivial by contrast. scope ref would bevirtually identical to ref given that ref already has to guarantee that the variable being referenced doesn't get destroyed before the ref is.No, there are other bonuses: - It mechanically enforces a given argument will not have a pointer taken and escape. - It gives the extra information to the programmer who can better reason about API intent. - It allows 'ref' alone to retain an important function where it may escape a pointer if it wants to.The only real difference would be that scope would presumably additionally prevent doing system stuff like taking the address of the ref. I don't see how this buys us anything.Yes this is an advantage, I listed it above. It buys the programmer some additional flexibility/choice. I agree that auto ref isn't a great name, but it's what we already have, andusing it on non-templated functions would be using it for exactly what it was designed for in the first place and how it's described in TDPL.I'm not going to change my position that it makes no sense, and is misleading/confusing without some real arguments, which nobody seems able to provide. auto ref has already shown to create misunderstanding in the minds of non-super-technical programmers. Syntax should encourage correct understanding. The only reasonthat it's not what we have now is because Walter misunderstood it when he implemented it.If that's true, I'm glad he did. Or perhaps he never agreed to it in the first place. His misunderstanding suggests that he never actually agreed to the initial proposal that you refer to.Considering the key argument against 'scope ref' is that people don't want...so, we don't have it? And you wereto require more attributes to make use of it, I don't see how 'auto ref' satisfies this argument either.Because we already have auto ref. It just hasn't been implemented on non- templated functions yet even though it was supposed to be.proposing not only using scope ref with a particular meaning on function parameters but also adding it as a return type, which it definitely isn't know regardless of what scope of function parameters does or doesn't do.I'm (note: not originally my proposal, although I shared the thought) proposing that scope has a uniform meaning as applied to any variable anywhere, where it's a local, or an argument (just a local declared in a different spot), or a return value (yet another local, declared in a different spot), the meaning is consistent. And it'seven questionable that scope as originally intended can be properly implemented anyway....so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.Thus, I am quite happy happy with 'ref', it can be made safe, satisfies theI'm not bothered by that personally, but if it's critically important, then we start arguing scope ref again. Otherwise I am happy to accept 'ref' with new added safety.argument above, and this seems like a very good start that we might actually all agree on.As has already been discussed in this thread, it will introduce maintenance problems if ref accepts rvalues.
May 09 2013
On 05/10/2013 12:42 AM, Manu wrote:On 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com <mailto:jmdavisProg gmx.com>> wrote: ... So, using scope ref to solve that problem makes no sense, and the changes that we've proposed to make to ref to make it safe pretty much make scope unnecessary. I agree, that's why I'm also happy with 'ref' alone, but I still feel it doesn't communicate as much information, which is a trivial by contrast. scope ref would be virtually identical to ref given that ref already has to guarantee that the variable being referenced doesn't get destroyed before the ref is. No, there are other bonuses: - It mechanically enforces a given argument will not have a pointer taken and escape.This is the same for 'ref' in safe code in the final implementation.- It gives the extra information to the programmer who can better reason about API intent.No. scope is supposed to restrict escaping (in some way that is still to be determined). If it is overloaded to also mean 'accept rvalues', then reasoning about API intent is actually harmed, because it will not clear whether 'scope' was added to restrict escaping alone or also to accept rvalues.- It allows 'ref' alone to retain an important function where it may escape a pointer if it wants to.In safe code? No way.The only real difference would be that scope would presumably additionally prevent doing system stuff like taking the address of the ref. I don't see how this buys us anything. Yes this is an advantage, I listed it above. It buys the programmer some additional flexibility/choice. I agree that auto ref isn't a great name, but it's what we already have, and using it on non-templated functions would be using it for exactly what it was designed for in the first place and how it's described in TDPL. I'm not going to change my position that it makes no sense, and is misleading/confusing without some real arguments, which nobody seems able to provide. auto ref has already shown to create misunderstanding in the minds of non-super-technical programmers.I think that this should be an oxymoron.Syntax should encourage correct understanding.It can't. FWIW overloading scope fails this requirement badly.... As has already been discussed in this thread, it will introduce maintenance problems if ref accepts rvalues. I'm not bothered by that personally, but if it's critically important, then we start arguing scope ref again. Otherwise I am happy to accept 'ref' with new added safety.Either scope ref will turn out to be liable to similar issues, or a keyword will have been wasted.
May 09 2013
On 10 May 2013 09:09, Timon Gehr <timon.gehr gmx.ch> wrote:On 05/10/2013 12:42 AM, Manu wrote:Fine. safe is expected to place more restrictions. But it's still something that people need to do sometimes in un- safe code. - It gives the extra information to the programmer who can betterOn 10 May 2013 08:09, Jonathan M Davis <jmdavisProg gmx.com <mailto:jmdavisProg gmx.com>> wrote: ... So, using scope ref to solve that problem makes no sense, and the changes that we've proposed to make to ref to make it safe pretty much make scope unnecessary. I agree, that's why I'm also happy with 'ref' alone, but I still feel it doesn't communicate as much information, which is a trivial by contrast. scope ref would be virtually identical to ref given that ref already has to guarantee that the variable being referenced doesn't get destroyed before the ref is. No, there are other bonuses: - It mechanically enforces a given argument will not have a pointer taken and escape.This is the same for 'ref' in safe code in the final implementation.The fact that it can safely receive a temporary is implicit if there is a guarantee that it will not escape. Why should explicit syntax exist to say "I accept rvalues", and not "I can safely receive temporaries"? The rvalue case is a subset, and I see no reason for it to receive special treatment. - It allows 'ref' alone to retain an important function where it mayreason about API intent.No. scope is supposed to restrict escaping (in some way that is still to be determined). If it is overloaded to also mean 'accept rvalues', then reasoning about API intent is actually harmed, because it will not clear whether 'scope' was added to restrict escaping alone or also to accept rvalues.Sure, people expect restrictions in safe code. But it's still something that people need to do sometimes in un- safe code. The onlyescape a pointer if it wants to.In safe code? No way.What? Syntax should encourage correct understanding.real difference would be that scope would presumably additionally prevent doing system stuff like taking the address of the ref. I don't see how this buys us anything. Yes this is an advantage, I listed it above. It buys the programmer some additional flexibility/choice. I agree that auto ref isn't a great name, but it's what we already have, and using it on non-templated functions would be using it for exactly what it was designed for in the first place and how it's described in TDPL. I'm not going to change my position that it makes no sense, and is misleading/confusing without some real arguments, which nobody seems able to provide. auto ref has already shown to create misunderstanding in the minds of non-super-technical programmers.I think that this should be an oxymoron.It's not an overload, it's a natural extension of the concept. Using auto is an overload! As has already been discussed in this thread, it will introduceIt can't. FWIW overloading scope fails this requirement badly.I don't understand? Can you expand this comment?maintenance problems if ref accepts rvalues. I'm not bothered by that personally, but if it's critically important, then we start arguing scope ref again. Otherwise I am happy to accept 'ref' with new added safety.Either scope ref will turn out to be liable to similar issues, or a keyword will have been wasted.
May 09 2013
On Thursday, 9 May 2013 at 22:42:14 UTC, Manu wrote:And it'sHowever despite the elusiveness of a solution, it looks like we'll be able to implement auto ref as was originally intended. We may also be able to implement scope as was originally intended, but not if we use it for another purpose. In any event you may want to use scope ref to prevent escapes and also refuse to use rvalues, so it is not a good solution for that reason alone. --rteven questionable that scope as originally intended can be properly implemented anyway....so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.
May 09 2013
On 10 May 2013 09:20, Rob T <alanb ucora.com> wrote:On Thursday, 9 May 2013 at 22:42:14 UTC, 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. the scope ref proposal does not interfere with scope as originally intended, it is a natural extension of the concept... unless I don't understand scope as originally intended properly (which is possible, it's barely documented). In any event you may want to use scope ref to prevent escapes and alsoAnd it'sHowever despite the elusiveness of a solution, it looks like we'll be able to implement auto ref as was originally intended. We may also be able to implement scope as was originally intended, but not if we use it for another purpose.even questionable that scope as originally intended can be properly implemented anyway....so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref.refuse to use rvalues, so it is not a good solution for that reason alone.Why? Why would a function want to receive a temporary but not an implicit temporary?
May 09 2013
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 09 2013
10-May-2013 04:18, Manu пишет:On 10 May 2013 09:20, Rob T <alanb ucora.com <mailto:alanb ucora.com>> wrote: On Thursday, 9 May 2013 at 22:42:14 UTC, Manu wrote: And it's even questionable that scope as originally intended can be properly implemented anyway. ...so, the problem is no different than 'auto ref' as you mention above. It's not implemented as drafted, and we're debating what's actually correct. Clearly the draft was incomplete in both cases. I only support the proposal (from others) that scope ref makes so much more sense, and I think we've also proven it can be made to work syntactically without holes, which I don't believe is so for auto ref. However despite the elusiveness of a solution, it looks like we'll be able to implement auto ref as was originally intended. We may also be able to implement scope as was originally intended, but not if we use it for another purpose. Except that auto ref as originally intended seems to have been a flawed design, as evidenced by the massive waves this issue keeps creating.Simply put it wasn't ever implemented like it was meant to. When something doesn't exist it's hard to believe that its broken. In fact I expected it to mean what you seem to attribute to scope ref i.e. ~ as C++ const& minus logical const part. The desire to make 2 versions of function in template case is serving one use case only - perfect forwarding and IMO is hacky. Funnily tough this corner-case beast (for templates) is implemented and the chief usage (for normal functions) isn't. -- Dmitry Olshansky
May 10 2013
On Thursday, May 09, 2013 09:00:43 Steven Schveighoffer wrote:On Thu, 09 May 2013 04:20:02 -0400, Manu <turkeyman gmail.com> wrote:Given how restrictive const is in D, I think that it would be a mistake to make it so that the way to make a ref accept rvalues is by using const. - Jonathan M DavisAh yes, right, C++ fix() would need to be 'const ref&', but we decide in D that 'ref const()' is too restrictive.I have no problem with ref const accepting rvalues. As I understand it, Andrei's objection (and this may be wrong/incomplete) is that then there is no way to say you accept only lvalues as const ref and rvalues via non-ref (for performance reasons). But I think there is no technical reason preventing that with D.
May 09 2013
On Friday, May 10, 2013 10:38:47 Manu 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.Not being able to differentiate between when it's an rvalue and lvalue causes some problems, though Andrei understands those issues far better than I do. I've been able to come up with cases where it would be a problem if you could have ref on a local variable, but without that, I don't really understand where you end up with problems with const ref due to not being able to differentiate between rvalues and lvalues. It's easy to come up with cases with plain ref (e.g. the example in Ali's talk when a type's opAssign treated rvalues and lvalues differently, swapping guts with the rvalue and copying from the lvalue), but I'm not as clear on when it ends up being an issue with const. The fact that we don't allow ref on anything other than parameters and return types really simplifies things, so I'm not sure that the situation with const ref would be anywhere near as bad in D as Andrei thinks that it is in C++, but I don't know. However, regardless of all that, the primary problem that I see with using const ref to indicate that you want to accept rvalues by ref is the fact that D's const is so restrictive, and there are types where const just plain doesn't work for them. As such, it strikes me as a bad idea to require const in order to accept rvalues by ref even if there are no other problems with it. Now, you could end up with other weird problems if you used auto ref (assuming that we used auto ref for this) without const and the function _did_ mutate its argument (we already have that problem with auto ref and templates), but if you really want the extra protection and can afford it, you can always use const with it. The problem is the cases where you can't use const. We've been trying very hard to make it so that const is completely optional precisely because of how restrictive it is. Requiring it for this seems problematic to me. - Jonathan M Davis
May 09 2013
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 09 2013
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 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 - Jonathan M Davis
May 09 2013
On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Thursday, May 09, 2013 21:56:53 Steven Schveighoffer wrote:1. using UFCS is optional. c[i].fix() works as well as fix(c[i]). 2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.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 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 usedwas self-documenting. But I don't think it's something we could add as a requirement, too much code uses ref. I don't think it is bad to make it optional. The caller can put restrictions he desires on types just like the callee can. Consider that the author of fix could change his code to double fix(double), and now the code also is useless. We can't possibly guard against that, because lvalues also bind to non-ref. I tend to feel that any modification that adds or removes ref is a severe breaking change, and is so bad that it shouldn't be done without also changing the name of the function anyway. -Steve
May 09 2013
On Thursday, May 09, 2013 22:33:09 Steven Schveighoffer wrote:On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis <jmdavisProg gmx.com>That would be an option, but as I explain belong, I think that having ref be optional at the call site is a bad idea.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.1. using UFCS is optional. c[i].fix() works as well as fix(c[i]).2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.This has come up on the newsgroup before, and I really do think that having it as optional is worse than not having it. If it's required, then when you see that an argument is marked with ref, you know that the parameter is a ref parameter, and when you see an argument without ref, you know that the parameter is not a ref parameter. And the compiler enforces both, so you always catch it when the function changes. On the other hand, if ref at the callsite is optional, then the lack of ref at the call site means absolutely nothing. You're still going to have to look at every single function signature to determine if it takes a ref argument or not, meaning that the lack of ref at the call site buys you nothing at best and harms you at worst, because it gives you a false sense of security that the argument is not being passed by ref. Programmers would have to be 100% consistent in using ref at the callsite to avoid that, and that's obviously not going to happen. You _do_ still get the gain that if you use ref at the call site, then your code will break if the parameter is no longer ref. But I think that the fact that the lack of ref means nothing while still giving the impression that the parameter is not a ref parameter is worse than never having ref at the call site. - Jonathan M DavisIf it weren't for UFCS, I probably would be in favor of requiring it at the 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 usedwas self-documenting. But I don't think it's something we could add as a requirement, too much code uses ref. I don't think it is bad to make it optional.
May 09 2013
On Friday, 10 May 2013 at 04:44:29 UTC, Jonathan M Davis wrote:Why ? Range behavior is undefined when passed by value.2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.On the other hand, if ref at the callsite is optional, then the lack of ref at the call site means absolutely nothing. You're still going to have to look at every single function signature to determine if it takes a ref argument or not, meaning that the lack of ref at the call site buys you nothing at best and harms you at worst, because it gives you a false sense of security that the argument is not being passed by ref. Programmers would have to be 100% consistent in using ref at the callsite to avoid that, and that's obviously not going to happen.It make sense when you pass by ref to modify things. In this case passing an rvalue don't make any sense. If you di it for performance, then this is yet another instance of the compiler rewriting things to improve performances.
May 09 2013
On Friday, May 10, 2013 07:01:21 deadalnix wrote:On Friday, 10 May 2013 at 04:44:29 UTC, Jonathan M Davis wrote:In some cases you don't care, but ranges are almost always passed by value, and when you're worried about ranges being reference types, you call save, meaning that if you're chaining, you're even more likely to passing an rvalue. And even if a range is a reference type, it's frequently not an lvalue, because it's frequently the return value of a function, and it's frequently the case that such return values can't be returned by ref, so requiring ref for UFCS would have a tendency to kill function chaining via UFCS.Why ? Range behavior is undefined when passed by value.2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.The only time that it would make sense to put ref at the callsite is if you intend to mutate the object (and the ref at the callsite is then intended to signal that to the programmer as well as get the compiler to verify that it's being passed by ref). If plain ref were to accept rvalues, then ref at the callsite would be pretty much meaningless. And it would never make sense to use it with something like auto ref. - Jonathan M DavisOn the other hand, if ref at the callsite is optional, then the lack of ref at the call site means absolutely nothing. You're still going to have to look at every single function signature to determine if it takes a ref argument or not, meaning that the lack of ref at the call site buys you nothing at best and harms you at worst, because it gives you a false sense of security that the argument is not being passed by ref. Programmers would have to be 100% consistent in using ref at the callsite to avoid that, and that's obviously not going to happen.It make sense when you pass by ref to modify things. In this case passing an rvalue don't make any sense. If you di it for performance, then this is yet another instance of the compiler rewriting things to improve performances.
May 09 2013
On Friday, 10 May 2013 at 05:01:22 UTC, deadalnix wrote:Why ? Range behavior is undefined when passed by value.Ugh, it is usually other way around, at least given current typical range implementations.
May 10 2013
On Fri, 10 May 2013 00:44:08 -0400, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Thursday, May 09, 2013 22:33:09 Steven Schveighoffer wrote:I misunderstood. You would not need ref at callsite because it's optional. So this actually is fine. You don't need to do anything. How would you prevent 'rvalue' ranges from going in? Don't use UFCS, and put ref at callsite. The problem with this whole rvalue/lvalue scheme is that rvalue and lvalue are not good terms to describe what we want. Just because you can't assign to an rvalue doesn't mean you can't assign to a member of that rvalue, it could be a pointer.On Thu, 09 May 2013 22:10:22 -0400, Jonathan M Davis <jmdavisProg gmx.com>That would be an option, but as I explain belong, I think that having ref be optional at the call site is a bad idea.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 optionalatthe callsite than to not have it at all.1. using UFCS is optional. c[i].fix() works as well as fix(c[i]).2. We could say UFCS has an implicit ref-at-callsite if needed, and will not compile for rvalues. If you think about it, 'this' is implicitly ref, and UFCS is calling like the first argument is the 'this' reference.That would make using UFCS with ranges generally illegal, as most range-based funtions do not have ref parameters, and you generally don't want them to be passed by ref.This has come up on the newsgroup before, and I really do think that having it as optional is worse than not having it. If it's required, then when you see that an argument is marked with ref, you know that the parameter is a ref parameter, and when you see an argument without ref, you know that the parameter is not a ref parameter. And the compiler enforces both, so you always catch it when the function changes.That is not the point of this suggestion. It would simply be to ensure changes from an lvalue to an rvalue don't compile. It would only be used in those cases. In other words, ref at callsite means "I have an lvalue". If that changes, no compilation. -Steve
May 10 2013