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