April 02, 2008
On 02/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> globby classes are equivalent to muty classes because in both cases I am
>  storing information outside the const scope.  In globby classes, I'm storing
>  it in a global AA that has an element for each instance of the class that's
>  created.  In muty classes, I'm storing it in a member variable with the
>  class.  In all cases, it's not considered to be a state of the class, it's
>  just that for muty classes, I'm storing it with the class (and reaping the
>  benefits of not having to manage a globally stored AA).
>
>  I can prove that muty classes are equivalent to globby classes:
>
>  class muty
>  {
>     mutable X x;
>  }
>
>  is equivalent to
>  class globby
>  {
>    X x() const {...}
>    X x(X newvalue) const {...}
>  }

So you're mimicking mutable fields with a global AA. Gotcha.

There are several problems with this, including:
(1) the global variable is visible to at least the entire module, so
something else might modify it when you're not looking.
(2) are we really sure that modifying an AA is an atomic operation? I'm not.


It's also worth mentioning that you won't be able to modify global variables in a pure function, so the ability to mimic mutable member variables is lost at that point.
April 02, 2008
Janice Caron Wrote:

> Logical const in mutually incompatible with transitive const. No object can be simultaneously both fully transitive and have reachable mutable fields. It's just impossible. (Here I define "reachable" as meaning "
> 

What's logical const anyway? The bitwise/mutable definition is skewed in the context of D.

Saying that is logical const :

class C
{
   mutable int a;
   int f() const { ++a; }
}

while this isn't:

class C
{
   static int a;
   int f() const { ++a; }
}

is kind of a reach.

Proposition are dismissed because they exhibit logical const behavior, like it's some kind of disease. Those propositions merely point out that D const is just logical const anyway (even if it pretends not to be).

At the function level, const/invariant is nothing more that an abstraction. It just means that the method will run in some "protected mode". Some data reachable trough const function are immutable, some are not. Even with the transitive rule. Even without mutable members.

At the data level, const/invariant is enforced (bitwise const).

The problem with C++ const is not that it is logical. The difference is that C++ const is basically just an annotation, while D has some potential for enforcement (data are bitwise const).


D
- Functions => logical const.
- Data => bitwise const.


C++
- Functions => logical const.
- Data => const just a annotation.



April 02, 2008
On 02/04/2008, guslay <guslay@gmail.com> wrote:
> What's logical const anyway?

By my definition, a C++ class is logically const, if and only if at least one of its member variables is declared using the keyword "mutable".

If other people use the phrase to mean something different, that probably explains much of the confusion.
April 02, 2008
Janice Caron wrote:
> (1) const does not guarantee thread safety
> (2) thread safety requires const guarantees

Steven is arguing that thread safety does not require transitive const
guarantees.
It was already established that pure functions will not have access to
global variables. If you add the rule that pure functions cannot access the
mutable part of a const object, const and pure work together to provide
thread safety just as well as with fully transitive const.

That's where the examples comparing transitive const with global variables to logical const with mutable members originated:

int x;
class C
{
  void foo() const { x++; } // const but can't be pure
  void bar() pure { /* automatically safe */ }
}

versus

class C
{
  mutable int x;
  void foo() const { x++; } // const but can't be pure
  void bar() pure
  { /* can't do anything in here you couldn't have done above */ }
}

April 02, 2008
Sean Kelly wrote:
> I agree that transitive const can achieve more demonstrably correct code
> but I don't think it follows that this will necessarily improve productivity.
> My experience with const in C++ is that it actually reduces productivity
> because I spend more time dealing with the misapplication of const than
> I gain from whatever bugs the application of const may have prevented.
> In fact, in all my time as a programmer I can't think of a single instance
> where I've encountered a bug that could have been prevented through
> the use of const.

C++ const is more of a suggestion than anything verifiable by the compiler and static analysis. This will become more obvious as C++ tries to compete in the multiprogramming arena. The C++ compiler *cannot* help with multiprogramming issues; the C++ programmer is entirely dependent on visual inspection of the code, making C++ a tedious, labor intensive language for multiprogramming.

How many times have you looked at the documentation for a function API, and wondered which of its parameters were input and which were output?

When you talk about the "misapplication" of const, does that mean failure to understand what the specific API of a function is?
April 02, 2008
On 02/04/2008, Christian Kamm <kamm.incasoftware@shift-at-left-and-remove-this.de> wrote:
> Steven is arguing that thread safety does not require transitive const
>  guarantees.
> <snip>

Thank you. That was a perfect explanation. I get it now.


>  class C
>  {
>   mutable int x;
>   void foo() const { x++; } // const but can't be pure
>   void bar() pure
>   { /* can't do anything in here you couldn't have done above */ }
>  }

But you could equally well write it like this:

    class C
    {
        int x;
        void foo() { x++; }
        void bar() pure
        { /* can't do anything in here you couldn't have done above */ }
    }

If it can change, then don't call it const. Seems a simple enough rule to me.
April 02, 2008
On 02/04/2008, guslay <guslay@gmail.com> wrote:
>  Saying that is logical const :
> <snip>
>  while this isn't:
> <snip>
>  is kind of a reach.

From where I stand, it's true by definition. But I'll agree to use your definition of logical const for the rest of this post (but don't expect me to use it thereafter). So, by your definition a class which contains member functions which are declared const and which modify global variables, is logically const. Hmm... I called that "globby" in another post.


> Those propositions merely point out that D const is just logical const anyway (even if it pretends not to be).

D doesn't pretend not be support classes which contains member functions which are declared const and which modify global variables. By your definition, D does not pretend not to be logical const. (Now see why it's so confusing to have different definitions).


>  At the function level, const/invariant is nothing more that an abstraction.
I said that. No disagreement there. All it does is specify the constancy of the hidden function parameter known as "this".

>  At the data level, const/invariant is enforced (bitwise const).
Again, I said that. No disagreement there.

So apart from a little bit of terminology, we're agreeing.
April 02, 2008
"Janice Caron" wrote
> On 02/04/2008, Steven Schveighoffer wrote:
>> globby classes are equivalent to muty classes because in both cases I am
>>  storing information outside the const scope.  In globby classes, I'm
>> storing
>>  it in a global AA that has an element for each instance of the class
>> that's
>>  created.  In muty classes, I'm storing it in a member variable with the
>>  class.  In all cases, it's not considered to be a state of the class,
>> it's
>>  just that for muty classes, I'm storing it with the class (and reaping
>> the
>>  benefits of not having to manage a globally stored AA).
>>
>>  I can prove that muty classes are equivalent to globby classes:
>>
>>  class muty
>>  {
>>     mutable X x;
>>  }
>>
>>  is equivalent to
>>  class globby
>>  {
>>    X x() const {...}
>>    X x(X newvalue) const {...}
>>  }
>
> So you're mimicking mutable fields with a global AA. Gotcha.
>
> There are several problems with this, including:
> (1) the global variable is visible to at least the entire module, so
> something else might modify it when you're not looking.

Who cares?  I am in charge of my module, it's not possible for another author to change the AA in a way that I do not define.  If anything, this is an argument FOR logical const so I can have the compiler help me prevent this :)

> (2) are we really sure that modifying an AA is an atomic operation? I'm not.

I am really sure that modifying an AA is not an atomic operation, but that has no bearing on the proof.  Setting x in the mutable version is also not atomic.  They are still equivalent.

> It's also worth mentioning that you won't be able to modify global variables in a pure function, so the ability to mimic mutable member variables is lost at that point.

Again, if logical const were allowed, pure functions would not be able to modify the mutable portions of classes, so this is not a difference between muty and globby.

I think I may have not explained my opinion correctly on pure functions.  I believe transitive invariance is required for enforcable pure functions.  In order to enforce that, the compiler should be able to tell what is supposed to be invariant and what is not.  Having logical const be a possibility does not prevent this, because the compiler can just force the pure function not to use the mutable portions of the class/struct.

If mutable member variables were allowed, they would have the same restrictions for pure functions as any other external mutable variable.

-Steve


April 02, 2008
>>  class C
>>  {
>>   mutable int x;
>>   void foo() const { x++; } // const but can't be pure
>>   void bar() pure
>>   { /* can't do anything in here you couldn't have done above */ }
>>  }

Janice Caron wrote:
> But you could equally well write it like this:
> 
>     class C
>     {
>         int x;
>         void foo() { x++; }
>         void bar() pure
>         { /* can't do anything in here you couldn't have done above */ }
>     }
> 
> If it can change, then don't call it const. Seems a simple enough rule to me.

Yes, but now const's transitivity isn't a necessity for 'future multiprogramming features' anymore. Instead, we're discussing a const scheme where mutable members could be allowed, but aren't - for simplicity, consistency or some other reason.

I have not used D2 yet, so I'm not sure it is as restricting as some people suggest. From the outside transitive const certainly looks simpler and more useful than C++'s variant. However, if there are several genuine cases where escapes from transitivity would be advantageous, allowing exceptions should be considered.

April 02, 2008
"Christian Kamm" wrote
>>>  class C
>>>  {
>>>   mutable int x;
>>>   void foo() const { x++; } // const but can't be pure
>>>   void bar() pure
>>>   { /* can't do anything in here you couldn't have done above */ }
>>>  }
>
> Janice Caron wrote:
>> But you could equally well write it like this:
>>
>>     class C
>>     {
>>         int x;
>>         void foo() { x++; }
>>         void bar() pure
>>         { /* can't do anything in here you couldn't have done above */ }
>>     }
>>
>> If it can change, then don't call it const. Seems a simple enough rule to me.
>
> Yes, but now const's transitivity isn't a necessity for 'future
> multiprogramming features' anymore. Instead, we're discussing a const
> scheme where mutable members could be allowed, but aren't - for
> simplicity,
> consistency or some other reason.
>
> I have not used D2 yet, so I'm not sure it is as restricting as some
> people
> suggest. From the outside transitive const certainly looks simpler and
> more
> useful than C++'s variant. However, if there are several genuine cases
> where escapes from transitivity would be advantageous, allowing exceptions
> should be considered.

The point of this whole thread is that exceptions ARE possible now, so why must they be difficult?

-Steve