July 04, 2012
On Thursday, July 05, 2012 00:03:02 Timon Gehr wrote:
> On 07/04/2012 11:53 PM, deadalnix wrote:
> > Le 04/07/2012 20:40, Jonathan M Davis a écrit :
> >> On Wednesday, July 04, 2012 12:55:44 Tobias Pankrath wrote:
> >>>> Many languages does this (it doesn't mean it is the right thing to do). Do you know why this shouldn't be done ?
> >>> 
> >>> In C++ it was exception safety, wasn't it?
> >> 
> >> I believe that it was purely a question of speed. If popFront returns an
> >> element, then that element gets copied, and if you didn't need to
> >> access the
> >> element, then that's wasted cycles. You have to worry about exceptions in
> >> either case, depending on the what popFront is doing.
> >> 
> >> - Jonathan M Davis
> > 
> > If you return by reference, you get an overhead of 1 MOV instruction. This is ridiculous.
> 
> You get an 'overhead' of calling front, which is potentially unbounded.
> 
> struct Map(...){
>      ...
>      @property auto ref front() { return f(otherRange.front); }
> }

Not to mention, in many cases, you _can't_ return a ref like deadalnix suggests, because that would require that front be returning an lvalue, and it often isn't. And in the case of strings - which is the prime reason for this discussion in the first place - it definitely can't return an lvalue, because front is calculated (that and returning a value from popFront would make popFront much more expensive in the case where you _don't_ actually need to access front, so making popFront return an element doesn't help the string case at all).

- Jonathan M  Davis
July 05, 2012
If you really don't need the value, you could devise a "justPop" method that does not return (by the way, overloading by return type would be an amazing feature here). The idea is not "we should return a value everytime we pop", but "we should pop when we return a value".

-- 
Christophe
July 05, 2012
(grain of salt, I'm new to D.)

I'd vote for consumeFront being always available, because it's distinctly more convenient to call one function instead of two, especially when you expect that making a copy of front is cheap (e.g. a collection of pointers, numbers or slices). Ranges where 'front' returns a pointer to a buffer that popFront destroys (overwrites) are surely in the minority, right? So I hope they would be retrofitted to support consumeFront.

But, given that popFront is allowed to be destructive to the value of front, by re-using the same buffer (and that the proposed consumeFront might also be implemented with 'delayed destruction' to front), I am wondering how one is supposed to implement generic code correctly when this is unacceptable, e.g.:

void append(Range1,Range2)(Range1 input, ref Range2 output)
{
	// Usually works, unless input.popFront happens to be destructive?
	foreach(e; input) output ~= e;
}


July 06, 2012
On Thursday, July 05, 2012 23:59:47 David Piepgrass wrote:
> (grain of salt, I'm new to D.)
> 
> I'd vote for consumeFront being always available, because it's distinctly more convenient to call one function instead of two, especially when you expect that making a copy of front is cheap (e.g. a collection of pointers, numbers or slices). Ranges where 'front' returns a pointer to a buffer that popFront destroys (overwrites) are surely in the minority, right? So I hope they would be retrofitted to support consumeFront.
> 
> But, given that popFront is allowed to be destructive to the value of front, by re-using the same buffer (and that the proposed consumeFront might also be implemented with 'delayed destruction' to front), I am wondering how one is supposed to implement generic code correctly when this is unacceptable, e.g.:
> 
> void append(Range1,Range2)(Range1 input, ref Range2 output)
> {
> 	// Usually works, unless input.popFront happens to be
> destructive?
> 	foreach(e; input) output ~= e;
> }

That's generally just fine. The only ranges in Phobos which would have any problem with that would be the ones in std.stdio which reuse a buffer for front, and I wouldn't really expect other ranges to have that sort of problem unless they were doing the same thing. It mostly becomes an issue of knowing which ranges you have to be careful of that way, and being careful what you pass them to. So, I wouldn't worry about it all that much in general, but something like consumeFront would break them entirely, since if it were introduced, then presumably, it would be used fairly heavily by many range- based functions, and it's guaranteed not to work with them, whereas a situation like your code above would be fairly rare, and it would generally be clear to the user of a range like ByLine that the function that they're passing it to would be doing something like appending each element in it into a new range, which wouldn't work, so they'd take precautions, like wrapping it in a call to map which duped the elements before passing it to append.

- Jonathan M Davis
1 2 3 4
Next ›   Last »