Implementing tail-const in D

Simen Kjærås simen.kjaras at gmail.com
Tue Jan 23 09:36:03 UTC 2018


Since tail-const (more correctly called head-mutable) was 
mentioned here lately (in the 'I closed a very old bug!'[1] 
thread), I've been racking my brain to figure out what needs 
doing to make a viable solution.

Unqual is the standard way today to get a head-mutable version of 
something. For dynamic arrays, static arrays, pointers and value 
types, including structs without aliasing, thi works. For AAs, 
classes, and structs with aliasing, Unqual is the wrong tool, but 
it's the tool we have, so it's what we use.

Unqual has other uses, so HeadMutable!T should be a separate 
template. This means parts of Phobos will need to be reworked to 
support types not currently supported. However, given these types 
are not currently supported, this should not break any existing 
code.

While it is generally desirable for T to be implicitly castable 
to HeadMutable!T (just like const(int[]) is implicitly castable 
to const(int)[]), the rules for such implicit casting in the 
language today are inconsistent[2] and incompatible with alias 
this[3], opDispatch, opDot, subclassing, and constructors.

Instead of implicit casting, I therefore propose we use a method 
headMutable(), which will attempt to call the appropriate 
functions to do the conversion. With these two building blocks, 
we have what we need for tail-const (head-mutable) ranges and 
other constructs.

What does your code need to do to support HeadMutable? If you 
have a templated struct that holds an array or pointer, the type 
of which depends on a template parameter, you can define a 
function opHeadMutable that returns a head-mutable version. 
That's it.

If you use HeadMutable!T anywhere, you almost definitely should 
use headMutable() when assigning to it, since T might not be 
implicitly castable to HeadMutable!T.

So what does all of this look like? An example templated struct 
with opHeadMutable hook:

struct R(T) {
     T[] arr;
     auto opHeadMutable(this This)() {
         import std.traits : CopyTypeQualifiers;
         return R!(CopyTypeQualifiers!(This, T))(arr);
     }
}

This is the code you will need to write to ensure your types can 
be converted to head-mutable. opHeadMutable provides both a 
method for conversion, and a way for the HeadMutable!T template 
to extract the correct type.

The actual implementation of HeadMutable!T and headMutable is 
available here:
https://gist.github.com/Biotronic/67bebfe97f17e73cc610d9bcd119adfb


My current issues with this:
1) I don't like the names much. I called them Decay, decay and 
opDecay for a while. Name suggestions are welcome.
2) As mentioned above, implicit conversions would be nice, but 
that'd require an entirely new type of implicit conversion in 
addition to alias this, opDispatch, opDot and interfaces/base 
classes. This would require some pretty darn good reasons, and I 
don't think a call to headMutable() is that much of a problem.

Questions:
Is a DIP required for this? Should I create a PR implementing 
this for the range types in Phobos? What other types would 
benefit from this?

I welcome any and all... feck it. Destroy!

--
   Simen

[1]: 
https://forum.dlang.org/post/egpcfhpediicvkjuklwo@forum.dlang.org
[2]: https://issues.dlang.org/show_bug.cgi?id=18268
[3]: Alias this is too eager, and allows for calling mutating 
methods on the temporary value it returns. If alias this was used 
to allow const(int[]) to convert to const(int)[], 
isInputRange!(const(int[])) would return true.


More information about the Digitalmars-d mailing list