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