Argumnentation against external function operator overloading is unconvincing

ZombineDev via Digitalmars-d digitalmars-d at
Sun Sep 25 06:10:42 PDT 2016

On Sunday, 25 September 2016 at 10:58:24 UTC, pineapple wrote:

> It gets under my skin that length and opIndex and opSlice 
> produce different results in phobos' ranges depending on one's 
> current position in the range. This doesn't make sense to me, 
> and the only reason I can conceive of it having become how 
> ranges work throughout phobos is because that's how dynamic 
> arrays work if you force them to act as though they were ranges.

But which opIndex and which length? Those of the container, or 
those of the range? It doesn't make any sense to expect 
container[].takeExactly(7).length to be different than 7. If 
range.length returns the length of the container this would break 
every sensible algorithm out there. How would you even implement 
binary search ( 
It's completely wrong to expect indexing operations to work on 
the original container. What you do in situations where there is 
no container, such as generators (e.g. iota and data coming from the network? And how would you get the number of remaining elements in the range?

> Phobos' range facilities vomit when you try to deal with static 
> arrays, and they have to be coerced into dynamic arrays before 
> they can be dealt with. This is silly.

What's the problem here? Just slice them using the [] syntax 
(i.e. the analog of asrange in your library).

> In every single other language I've used, the concept of an 
> Iterable and an Iterator are distinct and very separate. An 
> Iterator is something that can be iterated over; an Iterable is 
> something which can produce an Iterator for iterating over its 
> contents. In D, arrays are Iterables, and phobos endeavors to 
> force them to be Iterators as well. It defies years of basic 
> design wisdom regarding how to differentiate a collection and 
> the means by which one enumerates the items in that collection.
> Arrays are Iterables which should be able to produce an 
> Iterator, in D's case a range. They should not themselves be 
> Iterators.

There's seems to be a misunderstanding what are D's ranges and 
how they're meant to be used. Containers such as static arrays 
(i.e T[n]) are Iterable-s whereas slices (i.e. T[]) are 
Iterator-s by your taxonomy. You get the Iterator from the 
iterable static array using the opSlice (i.e. []) method. Ranges 
(and in particular slices) are just positions in an array. The 
same analogy holds for the containers in std.container: you get a 
range using the container[], or container.opSlice() syntax. 
Regardless where the range points to and if it shrinks, the 
container.length stays the same, assuming you haven't added or 
removed any elements.

I guess your misunderstanding stems from the fact that you call 
T[] arrays. This however is wrong. T[] is just slice of an array. 
If you want to use arrays similar to those in other languages, 
use std.container.array : Array 

D's built-in dynamic arrays are hidden from you and you only get 
to interact with them by referring to their elements by using 
slices. For example, have a look at the following code:

import std.algorithm.comparison : equal;

int[] arr = [1, 2, 3, 4];

void append5(int[] s)
     s ~= 5; // 1)


assert (arr.equal([1, 2, 3, 4]));

As you can see in the example above, since int[] is just a 
slice/range, appending to it does not modify the underlying 
container. Instead a new container is created (*) and `s` is made 
to point to it in 1). If you were to use a "proper" container 
such as std.container.array, the results would be different:

import std.algorithm.comparison : equal;
import std.container.array;

auto arr = Array!int([1, 2, 3, 4]);

void append5(Array!int s)
     s ~= 5;


assert (arr[].equal([1, 2, 3, 4, 5]));

For more information, I strongly suggest reading Steven's article 
: (*)

More information about the Digitalmars-d mailing list