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