On "A New Collections Framework for the Standard Library"

Jack Stouffer via Digitalmars-d digitalmars-d at puremagic.com
Thu May 18 18:22:50 PDT 2017


On Thursday, 18 May 2017 at 18:27:22 UTC, Andrei Alexandrescu 
wrote:
> Iterating over a container using e.g. foreach won't consume the
> container same as iterating over int[] won't consume the slice.

I don't understand why you're mapping the behavior of 
ranges/slices, which theoretically are shrinking views of data, 
onto containers of data which can grow or shrink. They seem 
antithetical to me.

The only reason foreach over a dynamic array doesn't consume the 
slice is because foreach uses index based iteration on static and 
dynamic arrays and not the std.range.primitives functions.

It's not evident to me that the non-consuming behavior makes 
sense to map onto something completely different like containers 
that won't even necessarily have index based access.

It seems odd to me that slices would be the design basis when 
slices

1. Are intentionally limited in scope design wise to be a 
shrinking view of data
2. Only make sense behavior wise for array containers
3. Their ability to grow is a GC oddity which may end up copying 
a whole lot of data depending on the origin of the slice

> There is no way to reclaim the original container. If you 
> create a container, you get a range positioned to "see" the 
> entire container. Once you popFront from that range, you lose 
> all access to the first element in the container, unless you 
> have a separate copy of the range. This is not new and not 
> different from:
>
> auto r = new int[10];
> r.popFront;
>
> What happens here is an array of 10 elements gets created. But 
> you don't get a type "array of 10 integers". You get "a slice 
> of integers, incidentally initialized to refer to the entire 
> array of 10 integers that was just created". Next, you decide 
> you don't care about the first element in the array. Once you 
> call r.popFront, access to that element is lost forever.

First off, how are you going to do something like a map over a 
immutable container then, as map uses the range primitives and 
not foreach? There's no reason in principal that that should 
cause an issue. But with that design popFront is mutating the 
underlying data, and therefore should not be allowed. But this 
works with the std.container design because popFront is mutating 
the range view which has no reason to be immutable.

Secondly, if I want to say, perform a map over the elements of a 
container for some output, and I want to do things with the 
elements later in the code, then I have to create a copy of the 
container and pass the original to map? If that's the case then 
why not just go with the std.container behavior of getting a 
range from a primitive if you end up having to create range 
copies? I mean it's fundamentally the same, but that way you 
don't have to worry about the copies changing the data of the 
container out from under you.

What happens to the copy of a container when the original 
modifies the underlying data? For example, what should this code 
print?

     auto container = Array!int(MAllocator.instance);
     container.put(iota(10));

     auto container_copy = container;
     container.popBackN(5);
     container.insertBack(1);

     container_copy.each!(a => write(a, ", "));

In my estimation, a properly designed containers library should 
print

     0, 1, 2, 3, 4, 1, 5, 6, 7, 8, 9

But from what you're describing, that the containers act like 
slices, the code with print

     0, 1, 2, 3, 4, 1, 6, 7, 8, 9


More information about the Digitalmars-d mailing list