September 11, 2007
Bruno Medeiros wrote:
> Sean Kelly wrote:
>> read operations.  But for better or worse, D const has a different aim.  The goal is largely to produce a system which allows for compiler optimization rather than to enforce some sort of logical restrictions on behavior.  I suspect this means that D apps won't look very much like C++ apps in terms of how const is used, and the overall utility for the average programmer may well be somewhat small.
> 
> I'm don't think that's entirely true. 'invariant' the keyword is indeed made to allow several compiler and program optimizations, but 'const' the keyword is really for enforcing program contracts and restrictions, thus improving safety. I don't even think 'const' the keyword has any use whatsoever for optimization.

True enough.  But the current design (ie. transitive and lacking 'mutable') is such that 'const' may have limited utility for UDTs where logical state is not equivalent to physical state.  I still think this is fine, as I'd prefer something simple and understandable, but I wonder whether this will be sufficient for enterprise programmers looking to switch from C++ to D.  Only time will tell, I suppose.


Sean
September 12, 2007
Janice Caron wrote:
> So the function was declared const, because /conceptually/ it was.
> 
> But the class had a mutable cache, declared with the C++ keyword "mutable"
> 
> Transitivity would wreck that.

You're right in that transitive const does not support the notion of "logical const" (which is the usual term for what you are referring to).

The problem with logical const, however, is it offers no semantic guarantees. I pass a logical const reference around, and the underlying data may or may not change. I have no guarantees one way or the other, and even worse, I can't even tell this is happening. "I" here meaning the compiler, and our hapless code auditor.

This just pulls the rug out from under:

1) functional programming
2) multithreaded programming
3) having a tightly specified interface

It goes back to painting a stripe across your hips and calling it a seatbelt.

Given this, it isn't any surprise that C++ is disastrously difficult to write functional & multithreaded programs in, and C++ mutability is one of the reasons why. In C++, you can write const this and const that and it doesn't mean jack squat. Many experienced C++ programmers will tell you that const is little more than a documentation aid.

In other words, you're quite right that transitive const totally wrecks using const to specify logical constness. And that's a good thing <g>.
September 12, 2007
Sean Kelly wrote:
> But for better or worse, D const has a different aim.  The goal is largely to produce a system which allows for compiler optimization rather than to enforce some sort of logical restrictions on behavior.

This is not the primary goal (it is a side effect of the primary goals). The goals are:

1) Make functional style programming possible, which will become extremely important as people will start using all those lovely cores.

2) Provide compiler enforced semantic guarantees, which improves the specification of interfaces between disparate parts of code.

3) Be able to treat invariant references as value types. This, for example, makes manipulating strings as easy as manipulating ints.

4) Makes COW programming checkable and enforceable. (COW programming is an important part of many styles of programming.) Without transitive const, COW is overly reliant on programmer discipline.

> I suspect this means that D apps won't look very much like C++ apps in terms of how const is used, and the overall utility for the average programmer may well be somewhat small.

Yes, it will be used differently. Whether it is overall better or not only experience will tell.
September 12, 2007
On 9/12/07, Walter Bright <newshound1@digitalmars.com> wrote:
>
> Janice Caron wrote:
> > So the function was declared const, because /conceptually/ it was.
> >
> > But the class had a mutable cache, declared with the C++ keyword
> "mutable"
> >
> > Transitivity would wreck that.
>
> You're right in that transitive const does not support the notion of "logical const" (which is the usual term for what you are referring to).
>
> The problem with logical const, however, is it offers no semantic guarantees. I pass a logical const reference around, and the underlying data may or may not change. I have no guarantees one way or the other,


But you could guarantee that /non-private/ data will not change. And since non-private data is never seen outside the file in which it is declared, I don't understand why this is a problem.


and even worse, I can't even tell this is happening. "I" here meaning
> the compiler, and our hapless code auditor.


Providing you restrict "logical constness" to private variables, is that really true? Inside the module, the compiler knows everything. Outside the module, the private variables are irrelevant anyway as they can never be accessed in any way.


This just pulls the rug out from under:
>
> 1) functional programming


Functional programming isn't possible anyway unless the function can also guarantee that it won't modify /global/ variables, and I see no way of specifying that in the prototype.



2) multithreaded programming


Likewise, a function cannot be guaranteed to be completely threadsafe unless it guarantees not to modify global variables, and I see no way of specifying that in the prototype.

Even if you could, it's more complicated than that. A function might modify global variables and yet still be threadsafe - providing it uses mutexes to ensure it has exclusive access to the shared variables. A good example of this is writef(). The writef() function writes to standard output, and so /must/ modify some global state - but it's thread-safe because file access is all mutex locked.

Moreover, multithreaded programming might /require/ you to lock a mutex, do something, then unlock said mutex. The mutex itself needs to be modifiable - that is, "logically const".

More thought needs to be put into that one.



3) having a tightly specified interface


Interfaces are my concern. If an Interface specifies that a function be const (in the sense of non modifying member variables), then it would make a lot of sense that classes which implement that interface be allowed to assume the interface means "logical constness", for the reasons of all the use-cases given so far.



It goes back to painting a stripe across your hips and calling it a
> seatbelt.
>
> Given this, it isn't any surprise that C++ is disastrously difficult to write functional & multithreaded programs in, and C++ mutability is one of the reasons why. In C++, you can write const this and const that and it doesn't mean jack squat. Many experienced C++ programmers will tell you that const is little more than a documentation aid.


I /am/ an experienced C++ programmer, and I agree. However, in C++, it is possible to declare non-private members mutable - and to make matters worse, the function bodies of functions which modify that variable could be in any number of different source files scattered all over the place.

In D it's different. First off, I do suggest restricting "logical constness" to private variables only. Secondly, all the function defintions which can access those source files are in one file, so the compiler knows everything.


This means that, in D, when you declare a function as const, there would still be an absolute guarantee that nothing non-private could ever be changed by that function. Outside the module, that's all you need to know. Inside the module, the compiler knows all anyway.



In other words, you're quite right that transitive const totally wrecks
> using const to specify logical constness. And that's a good thing <g>.
>

But logical constness is a /necessary/ thing, and if you outlaw any way of doing it legitimately, then people will do it by "cheating". By (for example), storing state in global variables. The simplest example I can come up with is that random number one. Recall:

 class RandomNumberGenerator;
 {
     private long seed;

     const int rand() /* guarantees not to modify any non-private member
variables */
     {
         seed = f(seed);
         return (seed >> 32);
     }
 }

So then I'd have to change it to something like:

 private long seed;

 private long getSeed()
 {
     lockMutex();
     long s = seed;
     unlockMutex();
     return s;
 }

 class RandomNumberGenerator;
 {
     const int rand()
     {
         seed = f(getSeed());
         return (seed >> 32);
     }
 }


And I'd end up with something that was much nastier than the very thing you are trying to avoid.


September 12, 2007
Walter Bright wrote:
> Janice Caron wrote:
>> So the function was declared const, because /conceptually/ it was.
>>
>> But the class had a mutable cache, declared with the C++ keyword "mutable"
>>
>> Transitivity would wreck that.
> 
> You're right in that transitive const does not support the notion of "logical const" (which is the usual term for what you are referring to).
> 
> The problem with logical const, however, is it offers no semantic guarantees. I pass a logical const reference around, and the underlying data may or may not change. I have no guarantees one way or the other, and even worse, I can't even tell this is happening. "I" here meaning the compiler, and our hapless code auditor.
> 
> This just pulls the rug out from under:
> 
> 1) functional programming
> 2) multithreaded programming
> 3) having a tightly specified interface
> 
> It goes back to painting a stripe across your hips and calling it a seatbelt.
> 
> Given this, it isn't any surprise that C++ is disastrously difficult to write functional & multithreaded programs in, and C++ mutability is one of the reasons why. In C++, you can write const this and const that and it doesn't mean jack squat. Many experienced C++ programmers will tell you that const is little more than a documentation aid.
> 
> In other words, you're quite right that transitive const totally wrecks using const to specify logical constness. And that's a good thing <g>.

Is logical const really that bad? What would happen if you required in the spec that any changes made to a logical const variable must be undoable without changing the semantics of the program?

Suppose we have a logical const object with internal state, A. We then call a logical const method, getResult(), which caches the result internally, making the object's internal state B. As long as the caching is done correctly, then A.getResult() == B.getResult(), so the compiler is free to substitute B for A, or A for B whenever it wants.

As far as I can see, this re-enables the benefits of transitive const for functional and multi-threaded programming, as the compiler is free to ignore any changes to the variable, just as it is for transitive const.


  -- Reiner
September 12, 2007
I'm fair sure that logical const is a real world requirement.

Take the classic example Shape, for example...

const Rectangle r = new Shape();
r.draw();

Whoops! Const-transitivity prevents r.draw() from compiling. Why? Because
Shape has a member variable Raster, and Rectangle.draw() calls
Raster.paintRectangle() or some such, which modifies the state of the
raster.

So what do you do? You could try changing the prototype to class Rectangle { override invariant draw(Raster raster); }

But now it still won't compile, because the abstract function
Shape.drawwasn't declared like that, so then you have to go back
another step and
change /that/ declaration to:
class Shape { abstract invariant draw(Raster raster); }

It's a solution which doesn't scale.

And there goes encapsulation...


September 12, 2007
Even something as simple as this needs logical const

class MyMathClass
{
    invariant int multiply(int x, int y) /* logically const */
    {
        debug logfile.writeLine("multiply.called");
        return x * y;
    }

    debug private Stream logfile;
}

You can't tell me that's not a real world need.


September 12, 2007
Janice Caron Wrote:

[snip]
> This just pulls the rug out from under:
> >
> > 1) functional programming
> 
> 
> Functional programming isn't possible anyway unless the function can also guarantee that it won't modify /global/ variables, and I see no way of specifying that in the prototype.
> 
> 
> 
> 2) multithreaded programming
> 
> 
> Likewise, a function cannot be guaranteed to be completely threadsafe unless it guarantees not to modify global variables, and I see no way of specifying that in the prototype.
> 
You obviously missed the discussion of the "pure" keyword, borrowed from Fortran-90 from the conference. This is a clear way of declaring that a function must not have any side-effects. Something I've long hoped to see in C++ too.

http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf

Regards,

Bruce.
September 12, 2007
Janice Caron Wrote:

> Even something as simple as this needs logical const
> 
> class MyMathClass
> {
>     invariant int multiply(int x, int y) /* logically const */
>     {
>         debug logfile.writeLine("multiply.called");
>         return x * y;
>     }
> 
>     debug private Stream logfile;
> }
> 
> You can't tell me that's not a real world need.
> 
> Even something as simple as this needs logical const<br><br>class MyMathClass<br>{<br>&nbsp;&nbsp;&nbsp; invariant int multiply(int x, int y) /* logically const */<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; debug logfile.writeLine(&quot;multiply.called&quot;);
> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return x * y;<br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; debug private Stream logfile;<br>}<br><br>You can't tell me that's not a real world need.<br><br>
> 
BTW if there any chance you can change your mail options not to post in both html and text. Every single message of yours is duplicated twice on http://www.digitalmars.com/webnews.

Thanks.
September 12, 2007
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:fc837t$29ni$1@digitalmars.com...
> Sean Kelly wrote:
>> But for better or worse, D const has a different aim. The goal is largely to produce a system which allows for compiler optimization rather than to enforce some sort of logical restrictions on behavior.
>
> This is not the primary goal (it is a side effect of the primary goals). The goals are:
>
> 1) Make functional style programming possible, which will become extremely important as people will start using all those lovely cores.

How does making const transitive allow for functional programming?  From what I understand (and that's not much) about functional programming, it's programming without side effects.  Consider:

int x;

class X
{
  const int void f()
  {
    x += 5;
    return x;
  }
}

int main()
{
  const X x1 = new X;
  const X x2 = new X;
  int y = x1.f() * x2.f();
}

Although X.f() is const, it has modified global data, so X.f() has side effects.  Yet both x1 and x2 are transitive-const, are they not?

Therefore, the compiler STILL cannot make any optimizations based on the fact that x1 and x2 are const.

I think in order to allow functional programming, you need to introduce a new type of const, for example fconst.  If a function is declared fconst, it is not allowed to change any data that is not local to the function, or call a non-fconst function, or to read non-fconst data.  If a piece of data is declared fconst, it cannot be changed.

I don't think you can make this restriction with const in general as I think it will severely limit current programming styles (not everyone wants to use functional programming, or else we'd all be using scheme).

> 2) Provide compiler enforced semantic guarantees, which improves the specification of interfaces between disparate parts of code.

As I showed above, this is not possible with simply transitive const.

>
> 3) Be able to treat invariant references as value types. This, for example, makes manipulating strings as easy as manipulating ints.

I don't understand this, perhaps someone can explain it further.  An example of how this helps would be good.

> 4) Makes COW programming checkable and enforceable. (COW programming is an important part of many styles of programming.) Without transitive const, COW is overly reliant on programmer discipline.

All I can find on the net about COW programming is the COW programming language.  I'm sure this isn't what you meant :)  can you please explain this further?

-Steve