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