output ranges: by ref or by value?
Steven Schveighoffer
schveiguy at yahoo.com
Sun Jan 3 18:42:14 PST 2010
We are arguing in circles, so I will just stop :)
I'll address the one point I think we both feel is most important below
On Sun, 03 Jan 2010 17:19:52 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> Steven Schveighoffer wrote:
>> On Sun, 03 Jan 2010 09:25:25 -0500, Andrei Alexandrescu
>> <SeeWebsiteForEmail at erdani.org> wrote:
>>
>>> Steven Schveighoffer wrote:
>>>
>>>> Not having opSlice be part of the interface itself does not preclude
>>>> it from implementing opSlice, and does not preclude using ranges of
>>>> it in std.algorithm. If I'm not mistaken, all functions in
>>>> std.algorithm rely on compile time interfaces. opApply allows for
>>>> full input range functionality for things like copying and outputting
>>>> where templates may not be desired.
>>>
>>> The point is how much container-independent code can someone write by
>>> using the Container interface. If all you have is a Container, you
>>> can't use it with any range algorithm.
>> The answer is, you don't. Ranges via interfaces are slower than
>> primitive functions defined on the interface, so use what's best for
>> interfaces when you have an interface and what's best for compile-time
>> optimization when you have the full implementation.
>
> I understand we don't agree on this. To me, making containers work with
> algorithms is a drop-dead requirement so I will rest my case.
>
> Nevertheless, I think there's one point that got missed. It's a tad
> subtle, and I find it pretty cool because it's the first time I used
> covariant returns for something interesting. Consider:
>
> interface InputIter(T) { ... }
> interface ForwardIter(T) : InputIter!T { ... }
>
> class Container(T) {
> InputIter!T opSlice() { ... }
> }
>
> class Array(T) : Container(T) {
> class Iterator : ForwardIter!T {
> ... all final functions ...
> }
> Iterator opSlice();
> }
>
> Now there are two use cases:
>
> a) The user uses Array specifically.
>
> auto a = new Array!int;
> sort(a[]);
>
> In this case, sort gets a range of type Array!(int).Iterator, which
> defines final primitives. Therefore, the compiler does not emit ONE
> virtual call throughout. I believe this point was lost.
>
> b) The user wants generality and OOP-style so uses Container throughout:
>
> Container!int a = new Array!int;
> auto found = find(a[], 5);
>
> This time find gets an InputIter!int, so it will use virtual calls for
> iteration.
>
> The beauty of this design is that without any code duplication it
> clearly spans the spectrum between static knowledge and dynamic
> flexibility - within one design and one implementation. This is the
> design I want to pursue.
I see why it is attractive to you. But to me, algorithms are not the main
driver for containers. One thing we both agree on -- when you know the
full implementation, algorithms from std.algorithm should be implemented
as fast as possible. Where we disagree is what is desired when the full
implementation is not known. I contend that the ability to link with
std.algorithm isn't a requirement in that case, and is not worth
complicating the whole world of ranges to do so (i.e. build std.algorithm
just in case you have reference-type ranges with a "save" requirement).
If you don't know the implementation, don't depend on std.algorithm to
have all the answers, depend on the implementation which is abstracted
correctly by the interface.
What this means is there will be some overlap in functions that are
defined on the interfaces and functions defined in std.algorithm. Most
likely the overlapping functions both point to the same implementation
(naturally, this should live in std.algorithm). This is for convenience
to people who want to use containers in the interface form, so they do not
need to concern themselves with the abilities of std.algorithm, they just
want containers that help them get work done.
There is still one flaw in your ideas for covariant types: even though
final functions can be used throughout and the possibility for inlining
exists, you *still* need to use the heap to make copies of ranges. That
was and still is my biggest concern.
I think the rest of this whole post is based on our disagreement of these
design choices, and it really doesn't seem productive to continue all the
subtle points.
[rest of growing disagreement snipped]
BTW, I use covariant return types freely in dcollections and I agree it
kicks ass. It seems like black magic especially when you are returning
possibly a class or interface which need to be handled differently.
-Steve
More information about the Digitalmars-d
mailing list