On 2011-12-27 02:47:50 +0000, Andrei Alexandrescu <seewebsiteforem...@erdani.org> said:

The perspectives here are extremely exciting. The question remains how to best package this awesome array of options to maximize coherence. So we have:

1. "Basic" containers - reference semantics, using classic garbage collection

2. Reference counted containers - still reference semantics, using reference counting

3. COW containers - value semantics, using reference counting to make copying O(1).

I like the idea, but...

I worry about fragmentation.

Say we have those three variant of each container and you write a function accepting a container, which one do you use for the parameter? The only type common to all three is "DListImpl", except it shouldn't work because:

1. you barred access to the underlying DListImpl in RefCounted (using opDispatch instead of alias this) 2. you want to spare users of writing "ref" every time they write a function prototype

Those two design constrains makes it impossible to have a common type independent of the "packaging" policy.

I worry also about const and immutable.

It's nice to have reference counting, but it works poorly with const and immutable. If a function declares it takes a const container, you'll have to pass the reference counted container by ref to avoid mutating the reference count, which goes contrary one of your goals.

- - -

Let me make a language suggestion that will address both of these problems.

Allow structs to be flagged so they are always passed by reference to a function, like this:

        ref struct DListImpl {…}

Now a function accepting a DListImpl will implicitly have it passed by "ref" with no unnecessary copy, and no need to mutate a reference count:

        void func(DListImpl list) {…}

Then you can use alias this in RefCounted giving access to a "ref DListImpl" which can be passed to functions always by ref, regardless of the packaging policy, and without the need to remember to write "ref" every time you write a function accepting a container (because it is a ref struct which is always implicitly passed by ref).

Functions that don't need to store a reference to the container won't have to care about whether the container they get is maintained using a reference counter, COW, or the GC. And those functions can accept a const container, or even a value-type container (which gets implicitly passed by ref).

So with implicit ref parameters for "ref struct" types you solve the issue of fragmentation and of passing const containers to functions.

The underlying principle is that how to efficiently pass a type to functions should be decided at the type level. You're trying to figure out how it should work for containers, but I think it should apply more broadly to the language as a whole.

But there is one more thing. ;-)

Implicit ref parameters could also solve elegantly the more general problem of passing rvalues by reference if you make them accept rvalues. Only parameters explicitly annotated with "ref" would refuse rvalues, those implicitly passed by ref would accept them.


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/

Reply via email to