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