View mode: basic / threaded / horizontal-split · Log in · Help
September 11, 2007
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
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
Re: Transitive const sucks
"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
1 2 3 4 5 6 7
Top | Discussion index | About this forum | D home