Thread overview
Formal request to remove "put(OutRange, RangeOfElements)"
Jul 18, 2012
monarch_dodra
Jul 18, 2012
Christophe Travert
Jul 23, 2012
monarch_dodra
Jul 23, 2012
Christophe Travert
Jul 23, 2012
Jonathan M Davis
July 18, 2012
I'm really very sorry that this is such a long post, but I'd like to make request for a serious change to Phobos, so I think it should be as complete as possible.

Thank you to those who have the patience to read it and consider it.


**************************************************
Abstract:

  This is a formal request for the deprecation of the the support for accepting ranges of elements for the function "put" ie (put(OutRange, RangeOfElements)).

  As convenient as this functionality may be is, it undermines the very definition of what constitutes an output range, and creates some fundamental problems when trying to deal with them.

  put(Range, RangeOfElements) doesn't actually bring anything to the table that would be missed.

**************************************************
Explanation:

  The problem is not "put" in and out of itself, but rather that it is the fundamental definition of what constitutes an output range. In particular, because you cannot extract elements from output ranges, and because an output range can potentially support multiple types, you cannot write:
  "ElementType!OutputRange" => this will yield "void" on a 'true' output range. Rather, you have to individually query if the range is an output for specific types:
  "isOutputRange!(OutputRange, SomeElement)"

  The catch is that the definition of "isOutputRange" is just "does "put(range, element)" compile". This means that essentially, as soon as a range is an output for elements, it becomes an output for ranges of elements, for ranges of ranges of elements, for ranges of ranges of ranges of elements, for ...

For example: int[] is an outputRange of int (obviously), but it is also defined as an output range of int[], int[2], and int[][]...
This is clearly not right.

**************************************************
Problems put creates for template restrictions:

  At it simplest, it prevents any algorithm from properly working with outputRanges. This is the definition of "copy":

Range2 copy(Range1, Range2)(Range1 source, Range2 target)
  if (isInputRange!Range1 && isOutputRange!(Range2, ElementType!Range1))

See the trap? This is perfectly legal code:
  int a[][][][];
  int b[];
  copy(a, b);

  Look bad? it gets worse. Imagine the function fill. It works the same as "copy", but it can also take an "element" as an argument. One would be tempted to write this pair of signatures:

void fill(Range1, Range2)(Range1 target, Element filler)
  if(isOutputRange(Range1, Element))

void fill(Range1, Range2)(Range1 target, Range2  filler)
  if(isInputRange!Range2 && isOutputRange(Range1, ElementType!Range2))

You can try to write this, but if you do, the code will never work. ANYTHING that matches the range fill, will also match the element based fill, since the target range supports "put" from a range... For example:
int[2] a = [1, 2];
int[] b = new int[](8);
fill(b, a); //ambiguous call...
//Are you copying a "range" or an "element"?
//Answer: Who know! Honestly: if b is an output range of a, WHICH IS THE RIGHT CALL?

  The only way to really avoid this problem (as is currently done in algorithm.d) is to add: "is(typeof(range.front = filler))", but this defeats the very requirement of outputRange (outputRange does not have front), and bumps the algorithm's requirements up to "inputRange".

  Long story short, it is not currently possible to write templates that reliably support an outputRange restriction.

**************************************************
Problems put creates for implementation:

  The big problem about put is that it makes outputRanges _lie_ about what they support. Here is an example at its simplest:

alias int[] A; alias int[] B;
A a = new int[](2);
B b = new int[](1);
assert(isOutputRange!(typeof(a), typeof(b)));
if(!b.empty)
  b.put(a);

  In this code:
*b is an output range of A.
*b is not empty.
*putting an A inside b creates a empty range exception !?

  While this example might look "cute", it is also unacceptable behavior. if b is a non-empty range that supports "elements A", then it _HAS_ to be able to support a put. In this case, b clearly does not support elements of type A.

  I'm sure you can imagine the kinds of problems this can caue for a template developer.

**************************************************
Why we don't need put(Range):

  Quite simply: because we have the higher order function. The convenience "put(Range)" is nothing more than a glorified algorithm. Instead of using put, the user should make a call to copy. "copy" is designed specifically for copying an input range into an output range. Why duplicate this functionality into put? Calling copy is much more honest and accurate about what is actually happening.

**************************************************
Problems regarding removing put(Range):

  In theory, the only problem this would create is breaking code which can be legitimately replaced by “copy” without any change.

  It *could* also break some templates, as some ranges would seize reporting themselves as outputRanges of Ranges, but, quite frankly, I think those algorithms are unsafe to begin with, and are operating on incompatible types.

**************************************************
Conclusion:

  I think I've shown the problems that put(Range) creates. Problems that are simply insurmountable. On the other hand, "e.put(range)" brings nothing that can't be rewritten as "copy(e, range)". For all these reasons, I think it would be best to get rid of it.
July 18, 2012
That sounds reasonable and justified. Let's wait to know if people maintaining legacy code will not strongly oppose to this.
July 23, 2012
Maybe I should put this request elsewhere? I'm not sure if there is a place where I should put this?

I know this is not a very exciting issue, but I think it is a very important to resolve, preferably sooner than later.

Maybe I'll try to help kick start this by listing all the modules that would need to be changed?
July 23, 2012
"monarch_dodra" , dans le message (digitalmars.D:173005), a écrit :
> Maybe I should put this request elsewhere? I'm not sure if there is a place where I should put this?
> 
> I know this is not a very exciting issue, but I think it is a very important to resolve, preferably sooner than later.
> 
> Maybe I'll try to help kick start this by listing all the modules that would need to be changed?

You can fill a bug report.

assert(isOutputRange!typeof(r), typeof(e) && !output.r);
output.put(e);

failing on the second line is worthy of attention.
And it makes fill fail IIRC.

-- 
Christophe
July 23, 2012
On Monday, July 23, 2012 17:33:07 monarch_dodra wrote:
> Maybe I should put this request elsewhere? I'm not sure if there is a place where I should put this?
> 
> I know this is not a very exciting issue, but I think it is a very important to resolve, preferably sooner than later.
> 
> Maybe I'll try to help kick start this by listing all the modules that would need to be changed?

If you've found a bug, then report it bugzilla. If you have an issue that needs discussion, then this is the right place. It's just that you don't always get people responding, especially if it's a complicated topic (and the fact that it's summer right now probably doesn't help due to an increased number of people on vacation). Personally, I intend to read through this and respond, but it's the sort of thing that obviously needs to be read through and thought through carefully before discussing, and I haven't had the chance to do that yet.

- Jonathan M Davis