Jump to page: 1 24  
Page
Thread overview
Collections question
Nov 27, 2015
Adam D. Ruppe
Nov 28, 2015
Jakob Ovrum
Nov 28, 2015
Kagamin
Nov 28, 2015
Adam D. Ruppe
Nov 28, 2015
Kagamin
Nov 27, 2015
Minas Mina
Nov 28, 2015
Luís Marques
Nov 28, 2015
bitwise
Nov 28, 2015
Jakob Ovrum
Nov 30, 2015
bitwise
Dec 01, 2015
Marc Schütz
Dec 01, 2015
Marc Schütz
Dec 02, 2015
deadalnix
Dec 02, 2015
ZombineDev
Dec 02, 2015
Marc Schütz
Dec 02, 2015
Marc Schütz
Dec 02, 2015
deadalnix
Nov 28, 2015
Jakob Ovrum
Nov 28, 2015
Jakob Ovrum
Nov 29, 2015
Jakob Ovrum
Nov 28, 2015
Dicebot
Nov 28, 2015
default0
Nov 28, 2015
Tobias Pankrath
Nov 28, 2015
Timon Gehr
Nov 28, 2015
Jonathan M Davis
Nov 28, 2015
Sebastiaan Koppe
Nov 29, 2015
Atila Neves
Nov 29, 2015
Jonathan M Davis
Nov 30, 2015
Tobias Pankrath
November 27, 2015
There's this oddity of built-in hash tables: a reference to a non-empty hash table can be copied and then both references refer to the same hash table object. However, if the hash table is null, copying the reference won't track the same object later on.

Fast-forward to general collections. If we want to support things like reference containers, clearly that oddity must be addressed. There are two typical approaches:

1. Factory function:

struct MyCollection(T)
{
    static MyCollection make(U...)(auto ref U args);
    ...
}

So then client code is:

auto c1 = MyCollection!(int).make(1, 2, 3);
auto c2 = MyCollection!(int).make();
auto c3 = c2; // refers to the same collection as c2

2. The opCall trick:

struct MyCollection(T)
{
    static MyCollection opCall(U...)(auto ref U args);
    ...
}

with the client code:

auto c1 = MyCollection!(int)(1, 2, 3);
auto c2 = MyCollection!(int)();
auto c3 = c2; // refers to the same collection as c2

There's some experience in various libraries with both approaches. Which would you prefer?


Andrei
November 27, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> 1. Factory function:

This is my preference for zero arg at least because the opCall thing is commonly misunderstood and confused with C++ default construction and we don't need to encourage that.

>     static MyCollection opCall(U...)(auto ref U args);
> auto c1 = MyCollection!(int)(1, 2, 3);

That syntax is the same as constructors... if that's what you want it to look like, we ought to actually use a constructor for all but the zero-argument ones which I'd use a static named function for (perhaps .make or perhaps .makeEmpty too)

But the opCall in cases where it conflicts with constructors ought to be discouraged.
November 27, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> There's this oddity of built-in hash tables: a reference to a non-empty hash table can be copied and then both references refer to the same hash table object. However, if the hash table is null, copying the reference won't track the same object later on.
>
> [...]

2, The opCall() one.
November 28, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> There's this oddity of built-in hash tables: a reference to a non-empty hash table can be copied and then both references refer to the same hash table object. However, if the hash table is null, copying the reference won't track the same object later on.

I keep hoping that that design decision would be changed...

> 1. Factory function:

Something I find deeply unsatisfying about D structs is their inability to reliably set non-trivial invariants, due to the lack of custom default ctors. If you are careful, you @disable this(), and provide a factory function that sets the invariant. But then, you aren't doing much more than renaming this() to make() or whatever. The issues with .init could be addressed without prohibiting a default ctor...
November 28, 2015
On Friday, 27 November 2015 at 20:25:12 UTC, Adam D. Ruppe wrote:
> That syntax is the same as constructors... if that's what you want it to look like, we ought to actually use a constructor for all but the zero-argument ones which I'd use a static named function for (perhaps .make or perhaps .makeEmpty too)

While I think this would be nice and explicit, it's bad for generic code, which would have to specialize to correctly call the nullary version.

November 28, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> There's this oddity of built-in hash tables: a reference to a non-empty hash table can be copied and then both references refer to the same hash table object. However, if the hash table is null, copying the reference won't track the same object later on.
>
> Fast-forward to general collections. If we want to support things like reference containers, clearly that oddity must be addressed. There are two typical approaches:
>
> 1. Factory function:
>
> struct MyCollection(T)
> {
>     static MyCollection make(U...)(auto ref U args);
>     ...
> }
>
> So then client code is:
>
> auto c1 = MyCollection!(int).make(1, 2, 3);
> auto c2 = MyCollection!(int).make();
> auto c3 = c2; // refers to the same collection as c2
>
> 2. The opCall trick:
>
> struct MyCollection(T)
> {
>     static MyCollection opCall(U...)(auto ref U args);
>     ...
> }
>
> with the client code:
>
> auto c1 = MyCollection!(int)(1, 2, 3);
> auto c2 = MyCollection!(int)();
> auto c3 = c2; // refers to the same collection as c2
>
> There's some experience in various libraries with both approaches. Which would you prefer?
>
>
> Andrei

Classes/real-ref-types dont act as you're describing, so why should these fake struct wrapper ref things act this way? This will likely achieve the exact opposite of what you're aiming for, by making something that's supposed to act like a reference type have different behaviour from D's built in ref types.

    Bit


November 28, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> There's some experience in various libraries with both approaches. Which would you prefer?

Well, I think we should recognize that they're the same thing but with different names. I don't have a strong preference for either, but I think the opCall approach might invite questions like "why is T t; different from auto t = T(); with this collection type?".

The current container library's `make` function has a neat feature (well, I'm biased here) where the element type doesn't have to be specified when construction arguments are provided:

auto arr = make!Array(1, 2, 3); // element type inferred to be `int`
auto arr = make!Array([42]); // Also for range construction

Naturally this doesn't work with nullary construction, but I think it's worth mentioning because this is not nearly as practical with static member functions. Of course the current model is not usable as-is because `make` uses an ugly hack when "making" empty struct containers.

November 28, 2015
On Saturday, 28 November 2015 at 06:59:35 UTC, bitwise wrote:
> Classes/real-ref-types dont act as you're describing

They do, actually.

class Collection(E) { ... }

Collection!E a; // null reference
auto b = new Collection!E(); // reference to empty collection

The only outlier is the associative array, which lazily initializes when operations are performed on null references.

November 28, 2015
On Friday, 27 November 2015 at 20:14:21 UTC, Andrei Alexandrescu wrote:
> There's some experience in various libraries with both approaches. Which would you prefer?

Another thing: wouldn't providing a custom allocator require a separate primitive? I am assuming that the allocator type won't be a template parameter, or at least that it will support IAllocator.

alias Allocator = ...; // some std.allocator type

Allocator alloc;

// empty collection of int with user-specified allocator
auto c = Collection!int.makeCustomAllocation(alloc);

// collection of one allocator using theAllocator
auto c = Collection!Allocator.make(alloc);

// empty collection of Allocator with user-specified allocator
auto c = Collection!Allocator.makeCustomAllocation(alloc);

The last two don't look like they could use the same name. A way around this would be to forego variadic construction in favour of range construction, but it would necessitate copying of those elements, whether with `only` or putting in an array etc.

If we need two names, then opCall becomes less attractive.
November 28, 2015
Factory please. Static opCall has always been nothing but trouble in my experience.
« First   ‹ Prev
1 2 3 4