www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 8483] New: Definition of isOutputList warped due to "put" implementation

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8483

           Summary: Definition of isOutputList warped due to "put"
                    implementation
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Phobos
        AssignedTo: nobody puremagic.com
        ReportedBy: monarchdodra gmail.com



This is a copy paste from the forum. It was originally written as both an
analysis and a proposed solution to a problem.

TL;DR:
"put" allows placing a range of objects inside an output range, which warps the
definition of "isOutputRange" The exact kind of errors it creates is this:

----
alias int[] A; alias int[] B;
A a = new int[](2);
B b = new int[](1);
assert(isOutputRange!(B, A));
if(!b.empty)
  b.put(a);
----
Here b is not empty as output range of A object, b is not empty, yet putting a
inside b creates an empty range exception.

But more generally, the entire problem lies with the fact that B is NOT an
output range of A objects.

Please find the original abstract in the next post.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 31 2012
parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=8483




**************************************************
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.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 31 2012