June 22, 2007
Leandro Lucarella wrote:
> Walter Bright, el 22 de junio a las 01:07 me escribiste:
>> Sean Kelly wrote:
>>> Walter Bright wrote:
>>>> http://www.digitalmars.com/d/const.html
>>> So in short, 'const' protects data and 'final' freezes references.  How do these two apply to an int declaration?
>>>    const final int x = 5;
>>> Is either a compiler error? are they synonyms in this case?
>> It's not an error, it's just redundant.
> 
> Shouldn't be better to be an error? So it's more clear that final makes
> sense only for reference types.

It shouldn't be an error.  With template functions, you often aren't sure what may be passed in, and may want to do something like this just to be safe:

void func(T)( const final T val ) { ... }



Sean
June 22, 2007
Daniel Keep wrote:
> 
> Frits van Bommel wrote:
>> Leandro Lucarella wrote:
>>> Walter Bright, el 22 de junio a las 01:07 me escribiste:
>>>> Sean Kelly wrote:
>>>>> Walter Bright wrote:
>>>>>> http://www.digitalmars.com/d/const.html
>>>>> So in short, 'const' protects data and 'final' freezes references. How do these two apply to an int declaration?
>>>>>    const final int x = 5;
>>>>> Is either a compiler error? are they synonyms in this case?
>>>> It's not an error, it's just redundant.
>>> Shouldn't be better to be an error? So it's more clear that final makes
>>> sense only for reference types.
>> Allowing it allows cleaner generic code, otherwise templates would often
>> have to check whether parameters were value or reference types.
> 
> Indeed; we already need to special-case functions that return void.

How so?  I thought "return void" was a legal statement?


Sean
June 22, 2007

Sean Kelly wrote:
> Daniel Keep wrote:
>>
>> Frits van Bommel wrote:
>>> Allowing it allows cleaner generic code, otherwise templates would often have to check whether parameters were value or reference types.
>>
>> Indeed; we already need to special-case functions that return void.
> 
> How so?  I thought "return void" was a legal statement?
> 
> Sean

Indeed it is.  But what if you need to actually *store* the return value?

I believe the problems I had with the coroutine stuff specifically were storing the return value and having functions that passed *in* a value of the return type (which obviously had to be written separately for functions with void since you can't have void parameters, either).

	-- Daniel
June 22, 2007

Sean Kelly wrote:
> ...
> 
> final
> const
> final const
> 
> And that's it.  'final' means a reference cannot be rebound, 'const' means the data cannot be altered (through the reference), and 'final const' means that both the reference is frozen and the data cannot be changed.  And that's it.  That the existence of invariant required the addition of parenthesis on class invariants just serves to strengthen the argument in my mind.  So in short, I'm just not convinced that 'invariant' provides enough utility to warrant the cost in complexity and broken consistency (unittest doesn't require parens, so why does invariant?).

What makes me uneasy about the above is that final isn't a type constructor: it's a storage class.  You're turning it into sorta-kinda-but-not-quite both.  So, you end up with:

final int x;		typeof(x) == int;
const int y;		typeof(y) == const int;
final const int z;	typeof(z) == final const int;

"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;

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.

	-- Daniel
June 22, 2007
Reply to Martin,

> After reading the articles by Walter referred to recently, I'm less
> confused that I was, but would still appreciate some further
> explanation.
> 
> So should "const foo bar = baz" be an error if foo is a value type, on
> the basis that one should be using "invariant" or maybe final? It it
> safe to have two things const/invariant that become identical
> depending on type?
> 
> And does "final" exist *solely* so that you can approximate
> "invariant" for objects as well as for structs and scalars? I can see
> its use when you want something to be invariant but with the
> stipulation that it must exist in memory and have an address. In which
> case, why have final vs invariant at all, rather than having final be,
> for example, "weak invariant" or "stored invariant" or something like
> that? From here, final just looks *semantically* like "a poor man's
> invariant".
> 

One use of final that I can think of is this (assuming I understand correctly)

int foo(final int* bar)
{
}

bar now acts like a c++ reference (can't be pointed to something else) but with the syntax of a pointer.


June 22, 2007
Daniel Keep wrote:

> 
> 
> Sean Kelly wrote:
>> ...
>> 
>> final
>> const
>> final const
>> 
>> And that's it.  'final' means a reference cannot be rebound, 'const' means the data cannot be altered (through the reference), and 'final const' means that both the reference is frozen and the data cannot be changed.  And that's it.  That the existence of invariant required the addition of parenthesis on class invariants just serves to strengthen the argument in my mind.  So in short, I'm just not convinced that 'invariant' provides enough utility to warrant the cost in complexity and broken consistency (unittest doesn't require parens, so why does invariant?).
> 
> What makes me uneasy about the above is that final isn't a type constructor: it's a storage class.  You're turning it into sorta-kinda-but-not-quite both.  So, you end up with:
> 
> final int x;          typeof(x) == int;
> const int y;          typeof(y) == const int;
> final const int z;    typeof(z) == final const int;
> 
> "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;
> 
> 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.
> 
> -- Daniel

I agree with Sean on the proposed system looking unnecessarily complex. I don't have a problem seeing your points regarding Sean's suggestion, but I still think he has shown that there still may be room for improvement, and since many (including me) think the full mechanics of this solution is somewhat hard to grasp, and many even think it is unnecessary to add to the language in the first place (I'm not one of those), we should strive even more than normal to find a solution that works and that is easy to use. I find it weird if such a great change should be solidified after the first alpha release in a new branch. Not everyone have the opportunity to test it the first few days after a release to give feedback.

-- 
Lars Ivar Igesund
blog at http://larsivi.net
DSource, #d.tango & #D: larsivi
Dancing the Tango
June 22, 2007
Don Clugston wrote:
> Better, I think, would be:

Some good suggestions. I made some changes.
June 22, 2007
Leandro Lucarella wrote:
> Even more, aren't:
> 
> const int x = 5;
> final int x = 5;
> invariant int x = 5;
> 
> all the same?

No, because finals exist in memory, while const/invariants do not. Const/invariant declarations also require their initializers to be evaluated at compile time, whereas final can do them at run time.

The const & invariant cases are the same, as they are the degenerate cases. It's sort of like:

int x = 1;
uint y = 1;

both represent 1.
June 22, 2007
Sean Kelly wrote:
> Walter Bright wrote:
>> In C++, sometimes const means invariant, and sometimes it means readonly view. I've found even C++ experts who don't know how it works.
> Odd.  The C++ system always seemed extremely simple to me.

It isn't. I run into people all the time who are amazed to discover that const references can change. Few understand when const is invariant and when it isn't. I've never even seen anyone mention the problem where the non-transitive const destroys any hope of having FP like capabilities in C++.


> I personally find the use of three keywords to represent three overlapping facets of const behavior to be very confusing, and am concerned about trying to explain it to novice programmers.  With three keywords, there are six possible combinations:
> 
> final
> const invariant
> final const
> final invariant
> const invariant
> final const invariant

Probably the thing to do is simply outlaw using more than one.

> That some of these may be redundant just serves to further confuse the issue in my opinion.  So I wondered whether one of the keywords could be done away with.  Previously, you said 'invariant' may only apply to data whose value can be determined at compile-time, thus I imagine it can only apply to concrete/data types (ie. not classes).  Assuming this is true, I wonder whether there is truly a point in having 'invariant' at all.  Assuming it were done away with, the system becomes much simpler to me:
> 
> final
> const
> final const
> 
> And that's it.  'final' means a reference cannot be rebound, 'const' means the data cannot be altered (through the reference), and 'final const' means that both the reference is frozen and the data cannot be changed.  And that's it.

It's missing the transitive nature of invariant.
June 22, 2007
Daniel Keep wrote:
> 
> Sean Kelly wrote:
>> ...
>>
>> final
>> const
>> final const
>>
>> And that's it.  'final' means a reference cannot be rebound, 'const'
>> means the data cannot be altered (through the reference), and 'final
>> const' means that both the reference is frozen and the data cannot be
>> changed.  And that's it.  That the existence of invariant required the
>> addition of parenthesis on class invariants just serves to strengthen
>> the argument in my mind.  So in short, I'm just not convinced that
>> 'invariant' provides enough utility to warrant the cost in complexity
>> and broken consistency (unittest doesn't require parens, so why does
>> invariant?).
> 
> What makes me uneasy about the above is that final isn't a type
> constructor: it's a storage class.  You're turning it into
> sorta-kinda-but-not-quite both.  So, you end up with:
> 
> 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?

> "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?

> 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