Proposal - Revised Syntax for const and final
Daniel Keep
daniel.keep.lists at gmail.com
Sat Sep 8 07:01:04 PDT 2007
Having gone over your proposal a few times, I have to admit that I don't
like it much. Sorry.
I just want to say, that this is intended to be constructive criticism,
not bashing. :)
Janice Caron wrote:
> I've had a little think about the whole const syntax, and I've come up
> with the following proposal. I don't know how well it will be
> received, because it's different from what D2.0 is now, and so will
> break 2.0 code if implemented. On the other hand, I don't think that's
> a bad thing, because I think the benefits outweight that.
Well, whether that's true is debatable, but D 2.0 is still very, *very*
alpha; sudden, drastic changes to the syntax are perfectly fine, so long
as they happen *now* rather than later. :)
> So, there is a rationale behind these choices. The philosophy is that
> the same syntax should apply to all types, regardless of whether they
> are value types or reference types. I also believe in the principle of
> least surprise. That means, even though there are lots of different
> things that need to be achieved, there should be a single, consistent
> approach which applies to all of them. Where syntax needs to be
> extended, it should be done in a way which is analogous to some
> existing D syntax.
I agree with your intentions here; having a simpler syntax would be
great, and consistency[1] is always nice.
> [snip]
>
> TOTAL constness --
> In general, if a variable x is declared to be of type const(T)
> then x is const, and x.member is const (and x.member.member, etc.)
That's called "transitivity".
> [snip]
>
> HEAD constness --
> In general, if a variable x is declared to be of type final(T)
> then x is const, but x.member is mutable (and x.member.member, etc.)
>
> final(C) c;
> c = new C(); /* Error */
> c.x = 1; /* OK* /
> c.p = new int[1]; /* OK */
> c.p[0] = 1; /* OK */
That's fine.
> final(S) s;
> s = S(); /* Error */
> s.x = 1; /* OK* /
> s.p = new int[1]; /* OK */
> s.p[0] = 1; /* OK */
This is just silly. You're completely ignoring how structures work.
auto y = &(s.x); // typeof(y) == int*
*(cast(S*)y) = S();
I just bypassed the head constness of s. Yes, I casted to do it, but
the point was that I *never* cast away the const-ness, because it was
never there. Even then, I could do the same thing with foreach and
s.tupleof, and you wouldn't even need a cast to do it.
You *cannot* have a structure const and not have its members const; it
doesn't make sense. You can either change it, or you can't.
> final(cdouble) z;
> z = 1; /* Error */
> z.re = 1; /* OK */
> z.im = 1; /* OK */
As above.
> final(int[][]) a; /* a is const array of array of int */
> a.length = 1; /* Error */
> a[0].length = 1; /* OK */
> a[0][0] = 1; /* OK */
Now you're contradicting yourself. You said that if something is final,
you can't change *it*, but you *can* change it's members. An array is
basically this:
struct Array
{
void* ptr;
size_t length;
}
Yet now you're saying that you can't change length.
Assuming this is a typo, it still doesn't work. If you change length to
be larger than the allocated capacity of the array, then D will
reallocate it and will effectively do this[2]:
a = new int[][](new_length);
Again, you can't have something be both mutable and not mutable at the
same time.
> [snip]
>
> TAIL constness --
> In general, if a variable x is declared to be of type const*(T)
> then x is mutable, but x.member is const (and x.member.member, etc.)
>
> const*(C) c;
> c = new C(); /* OK* /
> c.x = 1; /* Error */
> c.p = new int[1]; /* Error */
> c.p[0] = 1; /* Error */
So this is basically the *current* behaviour of const.
> const*(S) s;
> s = S(); /* OK* /
> s.x = 1; /* Error */
> s.p = new int[1]; /* Error */
> s.p[0] = 1; /* Error */
s = S(1, [1].dup);
I just bypassed the const*-ness.
> const*(cdouble) z;
> z = 1; /* OK* /
> z.re = 1; /* Error */
> z.im = 1; /* Error */
>
> const*(int[][]) a; /* a is array of const array of const int */
> a.length = 1; /* OK */
> a[0].length = 1; /* Error */
> a[0][0] = 1; /* Error */
Isn't this just const(int[])[]? It makes me uneasy that there's a whole
set of synonyms for a particular const type.
> [snip]
Honestly, I appreciate why you're doing this. The less rules and mental
exceptions you need to keep track of, the better. But D is a systems
language; that means you need to understand what is actually going on.
If we went and removed structs entirely from D, then this would actually
be a pretty cool way of doing things. But since we do have POD types, I
don't think it's going to work.
Also, you've said nothing on invariant. I assume you mean to treat
invariant in the same way as const?
-- Daniel
[1] That said, you have to be careful with consistency. You can easily
take it to absurd extremes if you're not careful.
[2] It also copies the old data across, but then the statement would
have gotten ugly. :)
More information about the Digitalmars-d
mailing list