range.save

Sönke Ludwig via Digitalmars-d digitalmars-d at puremagic.com
Fri Nov 20 08:34:59 PST 2015


Am 20.11.2015 um 16:37 schrieb Andrei Alexandrescu:
> On 11/20/2015 04:42 AM, Sönke Ludwig wrote:
>> I think that .save is a serious design flaw in the range API
>
> How would you redo it? -- Andrei

That's why I wrote that the alternatives had their own issues - I 
unfortunately don't have a solution to this either. Making usage errors 
fail loudly at runtime is the only one improvement I had in mind that 
wouldn't result in unacceptable code breakage.

Now if we could exclude reference type ranges from the picture* and 
ignore the fact that this would break tons of code, I think making input 
ranges non-copyable and using the postblit constructor to do the job of 
save() would be the right approach. Recently this was mentioned 
somewhere and the counter argument was that this wouldn't work for 
wrapper ranges:

     struct FilterRange(R) {
         private R _input;

         this(R input) {
             _input = input; // error: R not copyable
         }
         // ...
     }

But it does work!

     struct FilterRange(R) {
         private R _input;

         this(R input) {
             swap(_input, input); // fine!
         }
        // ...
     }

Passing a range into such a wrapper range can still be done directly in 
case of rvalues: FilterRange!MyRange(MyRange(...))

L-values require an explicit "move" call (which is a good thing):

     MyRange r;
     auto fr = FilterRange(r.move);

The lowering of "foreach" to "for" would also have to be adjusted 
accordingly.

The main drawback of using postblit for forward ranges is that it might 
happen that it gets invoked accidentally, resulting in hidden 
performance issues (IMO better than correctness issues, but anyway). 
That could either be mitigated by performing substantial work lazily 
instead of directly in this(this), or by keeping support for save() in 
addition to this(this), so that a valid forward range could still 
disable this(this) if it's costly and expose the copy functionality 
through save() instead.


* To still support reference type ranges, we could turn this around and 
define a copyref() method that creates a new range that references the 
same internal range object. The advantage is that this way around 
failure to call copyref() will result in an immediate error.


More information about the Digitalmars-d mailing list