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/egpcfhpediicvkjuk...@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.

Reply via email to