On Thu, 02 Dec 2010 17:02:42 -0500, Michel Fortin <[email protected]> wrote:

On 2010-12-02 16:14:58 -0500, "Steven Schveighoffer" <[email protected]> said:

On Thu, 02 Dec 2010 07:09:27 -0500, Michel Fortin <[email protected]> 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

Reply via email to