tail const

Steven Schveighoffer schveiguy at yahoo.com
Fri Dec 3 15:17:26 PST 2010


On Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin  
<michel.fortin at michelf.com> wrote:

> On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer"  
> <schveiguy at yahoo.com> said:
>
>> On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin   
>> <michel.fortin at michelf.com> wrote:
>>> My only concern with the "const(Object)ref" syntax is that we're  
>>> reusing  'ref' to denote an object reference with different  
>>> properties  (rebindable, nullable) than what 'ref' currently stands  
>>> for. But it  remains the best syntax I've seen so far.
>>  Where it would be beneficial is in mimicking the tail-const properties  
>> of  arrays in generic ranges.
>>  I have a container C, which defines a range over its elements R.
>>  const(R) is not a usable range, because popFront cannot be const.  So  
>> now  I need to define constR, which is identical to R, except the  
>> front()  function returns a const element.
>>  So now, I need the same for immutable.
>>  And now I need to triplicate all my functions which accept the ranges,  
>> or  return them.
>>  And I can't use inout(R) as a return value for ranges.
>>  If you can solve the general problem, and not just the class  
>> tail-const,  it would be hugely beneficial.
>>  My thought was that a modifier on const itself could be stored in the   
>> TypeInfo_Const as a boolean (tail or not), and the equivalent done in  
>> dmd  source itself.
>
> I'm not sure I get the problem. Can you show me in code?

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.

-Steve


More information about the Digitalmars-d mailing list