D const design rationale

Daniel Keep daniel.keep.lists at gmail.com
Fri Jun 22 20:00:39 PDT 2007



Sean Kelly wrote:
> Daniel Keep wrote:
>> final int x;        typeof(x) == int;
>> const int y;        typeof(y) == const int;
>> final const int z;    typeof(z) == final const int;
> 
> Hm.  Just to clarify, we both agree that the value of a final integer
> (ie. case 1 above) is effectively constant, correct?

Indeed.

>> "Wait; why is final part of the last type, but not the first?"  And what
>> does this mean if you want a class member with final-style binding, but
>> (final const) type semantics?
>>
>> final (final const(int*)) foo;
>>
>> As opposed to
>>
>> final invariant(int*) foo;
> 
> Perhaps I'm missing something, but I would rewrite this as:
> 
> final const int* foo;
> 
> Thus foo cannot be reassigned once set and the data foo refers to may
> not be changed through foo.  This is a slightly weaker guarantee than:
> 
> final invariant int* foo;
> 
> Which says that the data foo refers to is immutable, but I am skeptical
> that this guarantee actually matters much to users.
> 
> Or am I completely misunderstanding?  And why the parenthesis in the
> second declaration?

(When I wrote the below, I missed precisely what you were saying: the
problem with writing it at "final const int*" is that you've got both
final and const as a storage class; I assumed you were using them as a
type constructor.)

Ok, let's try this instead: in the current system, we have

class Foo
{
    final invariant(FunkyType) funky;

    this(bool superFunky)
    {
        if( superFunky )
            funky = cast(invariant) new FunkyType;
        else
            funky = some_global_invariant_funky;
    }
}

In this case, we have a final storage (so we can assign the value during
the ctor), and an invariant(FunkyType) value type.

Under your proposal, invariant is replaced with (final const); if we
want the above, it'd become:

    final final const(FunkyType) funky;

But how does the compiler tell that second final is storage class or
part of the type constructor?  Remember, we could be dealing with a
template that's just shoving "final" out the front of things, so we
can't assume two finals ==> one is a type constructor.  So we'd probably
have to do this:

    final (final const(FunkyType)) funky;

We can't use *just* "final const FunkyType funky" because then we'd have
an "invariant" storage class: which means the initialiser would have to
be a compile-time constant.  Incidentally, the above is also probably
equivalent to:

    final(final const(FunkyType)) funky;

Which really doesn't make any sense anyway...

>> I think the thing here is that you're shifting the complexity from
>> invariant into final; instead of invariant meaning two different things
>> with two very different appearances, you've got final meaning two
>> slightly different things with almost identical looks.
> 
> I only see final meaning one thing: that the associated value may not be
> reassigned.  For concrete types like integers this is effectively the
> same as const, but as Walter said, the integer would be addressable when
> final but not when const.  Perhaps this is the source of confusion?
> 
> Sean

Yes, but it *also* means "can assign to in a ctor".

Like Lars said, I do think that if there's a way to simplify or
consolidate this, then we should take it.  That said, I can see a use
for each of the various cases the new system allows for, and I don't
want to see any of them cut off in the name of "well *I'm* not going to
use it, and I don't like that keyword, so it has to go!"[1]. :)

	-- Daniel

[1] I'm not saying that's what you're doing, but I've heard that sort of
argument from a lot of people, so I've become somewhat defensive of the
new stuff: don't take away my new shinies!



More information about the Digitalmars-d mailing list