tail const
Michel Fortin
michel.fortin at michelf.com
Fri Dec 3 17:40:23 PST 2010
On 2010-12-03 18:17:26 -0500, "Steven Schveighoffer"
<schveiguy at yahoo.com> said:
> Here is an example range from dcollections (well, at least the
> pertinant part), for a linked list:
>
> struct Range
> {
> LinkNode *node;
> void popFront() { node = node.next; }
> }
>
> Now, let's say I have a LinkList instance (ignore the parameterized
> type). I want to pass this list to a function and ensure nothing
> changes in the list. So I create a function like this:
>
> void foo(const(LinkList) list)
> {
> auto r = list[]; // get a range from the list
> }
>
> Now, LinkList has a function like this:
>
> Range opSlice()
> {
> Range result;
> result.node = head;
> return result;
> }
>
> In order to satisfy constancy, I now have to do two things. I have to
> define a *different* range, because const(Range) doesn't work (popFront
> is not and cannot be const). Call it ConstRange. The second thing I
> have to do is now define a completely separate function for opSlice:
>
> ConstRange opSlice() const
> {
> ConstRange result;
> result.node = head; // result.node is tail-const
> }
>
> Now, I could possibly make Range just parameterized on the
> parameterized type, but I still have to create two separate types
> (whether generated by template or not).
>
> Finally, I have to repeat *all this* for immutable. And for all
> functions that take a range, or return a range.
>
> And this is the real kicker... it *still* doesn't work correctly. Observe:
>
> void foo(LinkList.ConstRange r)
> {
> }
>
> If I have a mutable LinkList called list, I can't do foo(list[]),
> because Range does not implicitly convert to ConstRange. I have to
> first convert list to a const(LinkList), and then use the slice
> operator.
>
> Now, if we have tail-const that I can apply to a struct, which just
> makes all references contained in the type tail-const, then this
> becomes very very easy (with proposed syntax from Tomek):
>
> @tail inout(Range) opSlice() inout
> {
> ...
> }
>
> One function, one Range defined, very simple, very elegant.
>
> Note that I can do all this if my range is an array (as it is in
> ArrayList) without modification to the compiler, because tail-const
> arrays are possible.
>
> All I want is to duplicate the implicit casting, and implicit typing,
> that arrays have with tail const. If we can have a solution that
> fixes the tail-const class problem *and* this problem, it will be two
> birds, one stone.
A fine explanation. Thank you.
I disagree about your proposed solution, but I recognize the problem.
The basic problem with your solution is that it creates a new kind of
const, a new kind of immutable and a new kind of shared. You should
realize that for the compiler to know the constness of member variables
inside a function, it'll have to know whether the 'this' pointer is
'const' or 'tail const'. So I think it's the wrong path.
The right path would be, I think, to parametrize the constness in the
type. A way to do this within the current constrains of the language
would be to make Range implicitly convertible to ConstRange, something
you should be able to do with "alias X this", X being a function
returning a ConstRange. The disadvantages are the duplication of the
opSlice function, and the inability to cast implicitly a ref Range to
ref ConstRange or a Range[] to a ConstRange[].
I have an idea that would fix those: make a template struct/class
instance implicitly convertible to another instance of that same
template if all members share the same memory layout and each member is
implicitly convertible to the same member of the other template.
--
Michel Fortin
michel.fortin at michelf.com
http://michelf.com/
More information about the Digitalmars-d
mailing list