Jump to page: 1 28  
Page
Thread overview
Transitive const sucks
Sep 11, 2007
Alex Burton
Sep 11, 2007
Regan Heath
Sep 11, 2007
Regan Heath
Sep 11, 2007
Alex Burton
Sep 11, 2007
Janice Caron
Sep 11, 2007
Alex Burton
Sep 11, 2007
Janice Caron
Sep 11, 2007
Sean Kelly
Sep 11, 2007
Janice Caron
Sep 11, 2007
Bruno Medeiros
Sep 11, 2007
Sean Kelly
Sep 12, 2007
Walter Bright
Sep 12, 2007
Janice Caron
Sep 12, 2007
Gregor Richards
Sep 12, 2007
Janice Caron
Sep 12, 2007
Janice Caron
Sep 12, 2007
Bruce Adams
Sep 12, 2007
Walter Bright
Sep 12, 2007
Sean Kelly
Sep 12, 2007
Walter Bright
Sep 12, 2007
Walter Bright
Sep 12, 2007
Janice Caron
Sep 12, 2007
Bruce Adams
Sep 12, 2007
Janice Caron
Re: Transitive const sucks (pure)
Sep 12, 2007
Bruce Adams
Sep 13, 2007
Ingo Oeser
Sep 12, 2007
Walter Bright
Sep 12, 2007
Walter Bright
Sep 12, 2007
Janice Caron
Sep 12, 2007
Janice Caron
Sep 12, 2007
Sean Kelly
Sep 13, 2007
Walter Bright
Sep 13, 2007
Bill Baxter
Sep 13, 2007
Robert Fraser
Sep 13, 2007
James Dennett
Sep 13, 2007
Janice Caron
Sep 13, 2007
Janice Caron
Sep 13, 2007
Christopher Wright
Sep 13, 2007
Sean Kelly
Sep 12, 2007
Reiner Pope
Sep 12, 2007
Janice Caron
Sep 12, 2007
Walter Bright
Sep 13, 2007
James Dennett
Sep 13, 2007
Walter Bright
Sep 14, 2007
James Dennett
Re: Transitive const sucks (ownership - aggregation v association)
Sep 13, 2007
Bruce Adams
Sep 13, 2007
Sean Kelly
Sep 12, 2007
Janice Caron
Sep 12, 2007
Bruce Adams
Sep 12, 2007
Walter Bright
Sep 12, 2007
Janice Caron
Sep 13, 2007
Walter Bright
Sep 13, 2007
James Dennett
Sep 13, 2007
Walter Bright
Sep 14, 2007
James Dennett
Sep 12, 2007
Walter Bright
Sep 13, 2007
Bruno Medeiros
Sep 11, 2007
Janice Caron
Sep 11, 2007
Janice Caron
Sep 11, 2007
Janice Caron
Sep 11, 2007
Janice Caron
Sep 11, 2007
Janice Caron
Sep 11, 2007
Christopher Wright
Sep 11, 2007
Bruno Medeiros
Sep 11, 2007
Janice Caron
Sep 12, 2007
Bruno Medeiros
Sep 12, 2007
Carsten Sørensen
Sep 12, 2007
Bruce Adams
Sep 12, 2007
Carsten Sørensen
Sep 12, 2007
Bruce Adams
Sep 12, 2007
Nathan Reed
September 11, 2007
While const is being reexamined....

I create an interface :

interface Server
{
    Data getData() const;
};

I then write half my application using a stub implementation of Server, which is refered to using const references, because this application doesn't modify the server (the interface has no non const members).
Then the time comes to implement the Server properly using a socket to communicate with a server.

interface Socket
{
    void send(const Data d);
    Data receive() const;
};

class ServerImplementation : Server
{
    Socket sock;
    Data getData() const
    {
        sock.send(request);
        return sock.receive();
    }
};

My application doesn't compile because I am refering to using const Server references every where, and it turns out the server class needs to modify it's socket to implement the getData method.

This is wrong.
The socket is not part of the ServerImplementation, it's just that D can't tell the difference between things that are part of a class and things that are not.
I now have to make the getData method non const and all the references to Server non const, and possibly not use any const references in my application again, as similar examples can be constructed for all sorts of things, and basically the const system does not work.

I think it's great to search for a better const system that c++, but if you have a great system for allowing compiler to optimise, but the programmer can't actually use the system to describe reality then it's not better at all.

The solution is to be able to say whether the socket is part of the server or not, then the const system can be made to work, as it did in c++.
I am not saying that c++ const is great but at least I can use it to describe reality (although it's cumbersome, and there are a lot of reasons why I am itching to convert all my stuff to D).

Sincerely,
Alex
September 11, 2007
Alex Burton wrote:
> While const is being reexamined....
> 
> I create an interface :
> 
> interface Server
> {
>     Data getData() const;
> };
> 
> I then write half my application using a stub implementation of Server, which is refered to using const references, because this application doesn't modify the server (the interface has no non const members).
> Then the time comes to implement the Server properly using a socket to communicate with a server.
> 
> interface Socket
> {
>     void send(const Data d);
>     Data receive() const;
> };
> 
> class ServerImplementation : Server
> {
>     Socket sock;
>     Data getData() const
>     {
>         sock.send(request);
>         return sock.receive();
>     }
> };
> 
> My application doesn't compile because I am refering to using const Server references every where, and it turns out the server class needs to modify it's socket to implement the getData method.
> 
> This is wrong.
> The socket is not part of the ServerImplementation, it's just that D can't tell the difference between things that are part of a class and things that are not.
> I now have to make the getData method non const and all the references to Server non const, and possibly not use any const references in my application again, as similar examples can be constructed for all sorts of things, and basically the const system does not work.
> 
> I think it's great to search for a better const system that c++, but if you have a great system for allowing compiler to optimise, but the programmer can't actually use the system to describe reality then it's not better at all.
> 
> The solution is to be able to say whether the socket is part of the server or not, then the const system can be made to work, as it did in c++.
> I am not saying that c++ const is great but at least I can use it to describe reality (although it's cumbersome, and there are a lot of reasons why I am itching to convert all my stuff to D).
> 
> Sincerely,
> Alex

I remember your example case from your earlier thread. :)

So, correct me if I'm wrong.  You want to be able to specify a reference which itself cannot change (perhaps a requirement, perhaps not?), but refers to an object which can.  And you want this reference to be usable inside a const method.

For the first I would suggest a new syntax like:

class Foo { int a; }
const(Foo&) pFoo;  //pFoo cannot change, pFoo.a can

This is analogous to my other idea[1].

For the second, either:

1. the compiler should assume that as the author used const on this reference, and intentionally applied it only to the reference itself, that changes made _via_ this reference are not covered by the const method contract.

2. we need a new keyword to indicate which references/pointers to mutable data can be used in a const method. i.e. "mutable"

in either case you would then be able to make changes to the referenced data from within a const method.

[1]See my recent posts to the "Const sucks" thread for the idea that:

class Foo { int a; }
const(Foo*) pFoo;  //pFoo can change, pFoo.a cannot

would declare a tail const class reference.

Regan
September 11, 2007
Regan Heath wrote:
> For the first I would suggest a new syntax like:
> 
> class Foo { int a; }
> const(Foo&) pFoo;  //pFoo cannot change, pFoo.a can

Actually, correction, this should probably be:

const(&Foo) pFoo;  //pFoo cannot change, pFoo.a can

The position of & in this case is important becase we don't want it confused with reference types but rather we're saying:

"the reference/pointer/address of this is const"
September 11, 2007
Your problem generalises to anything "streamy"

Going way back to the old C way of doing things
 FILE * f;
 fprintf(f, fmt, stuff);

You'd expect that to work, even if you copied from from inside a const struct, right? But then the type of f would have to change to

 const FILE * f;

which is C-speak for "f is a const pointer to mutable FILE". Now throwing in transitivity would stop it all working.

Moving forward in time to the modern era of objects, in general, you would want:

 stream.write(x);

to work even if the variable "stream" was const. (That is, if the /reference/ was const, not the data which is pointed to by the reference). The stack variable can be const, but the heap data needs to be mutable.

I don't have a solution, except to agree that "head const" does seem to be required after all.


September 11, 2007
Regan Heath Wrote:


> I remember your example case from your earlier thread. :)
> 
> So, correct me if I'm wrong.  You want to be able to specify a reference which itself cannot change (perhaps a requirement, perhaps not?), but refers to an object which can.  And you want this reference to be usable inside a const method.
> 
> For the first I would suggest a new syntax like:
> 
> class Foo { int a; }
> const(Foo&) pFoo;  //pFoo cannot change, pFoo.a can
> 
> This is analogous to my other idea[1].
> 
> For the second, either:
> 
> 1. the compiler should assume that as the author used const on this reference, and intentionally applied it only to the reference itself, that changes made _via_ this reference are not covered by the const method contract.
> 
> 2. we need a new keyword to indicate which references/pointers to mutable data can be used in a const method. i.e. "mutable"
> 
> in either case you would then be able to make changes to the referenced data from within a const method.

Yes essentially we need to be able to distinguish between mutable references and non mutable references. The latter being logically part of the class.
Note I would not suggest a mutable keyword that could be applied to class members that are structs or ints, like you can use in c++, as this is clearly bad for optimisation and is logically wrong.
But what I do want is essentially an ability to mark class references as mutable.

On the one hand I totally empathise with being able to write 'Socket sock;' for members that are classes possibly polymorphic. Instead of in c++ aggregate_ptr<Socket> sock, or scoped_ptr<Socket> sock or shared_ptr<Socket> sock;
Most of the needs of having these different pointer types was to do with memory management, which has been eliminated in D.
But there was also information on aggregation (part of relationships) in there.

The mutableness is just a property of the member not being part of the class, so what we really need is something like:

class Whole
{
    Part p;
    not_a_part_keyword NotAPart np;
};
September 11, 2007
Janice Caron Wrote:

> Your problem generalises to anything "streamy"

It then further generalised to anything "state machiney" :)

State machines are essential for computers to work. The processor itself is a state machine.
The stack has state.
If I was to implement a processor emulator in D I would have the same problem.

The idea that you can just 'get' a value from memory without modifing anything is an illusion created by the computer.

Alex
September 11, 2007
On 9/11/07, Alex Burton <alexibu@mac.com> wrote:
>
> The idea that you can just 'get' a value from memory without modifing anything is an illusion created by the computer.
>

Yes, it's even more obvious with a read than with a write.

Another interesting use-case involves caching. I'll spare you the boring details, but the essence is, I once had a class member function that did a lookup, much like an associative array, and the prototype would have been something like

 int lookup(int key) const;

This was C++, so the const was at the end. The thing is, all the data was in a file, and so the function had to open a file, read it, close the file, and return the data. Well, naturally, it cached some data internally to avoid too many file accesses. And - also naturally - I did not load the entire file into memory at constructor time because huge parts of it might not be needed at all. The file access was deferred until it was needed, and cached appropriately.

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.


September 11, 2007
On 9/11/07, Janice Caron <caron800@googlemail.com> wrote:
>
> The thing is, all the data was in a file, and so the function had to open a file, read it, close the file, and return the data. Well, naturally, it cached some data internally


These days, of course, the data source might not even be a file - it might be an resource you have to fetch from a remote site on the internet using HTTP. Caching is, occasionally, really necessary.


September 11, 2007
What all of these use-cases have in common is the fact that the state is private

Suppose that all class and struct members which were declared private, were always mutable, even if the class instance is const.

That would mean you could do this:

 class RandomNumberGenerator;
 {
     private long seed;

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

 const RandomNumberGenerator rng;
 writefln(rng.rand());

In C++, we would have declared seed "private mutable" to achieve the same thing, but in D we tend to assume that everything in the same module is "friendly", so why not just let "private" mean "private mutable"?


September 11, 2007
Janice Caron wrote:
> On 9/11/07, *Alex Burton* <alexibu@mac.com <mailto:alexibu@mac.com>> wrote:
> 
>     The idea that you can just 'get' a value from memory without
>     modifing anything is an illusion created by the computer.
> 
> 
> Yes, it's even more obvious with a read than with a write.
> 
> Another interesting use-case involves caching. I'll spare you the boring details, but the essence is, I once had a class member function that did a lookup, much like an associative array, and the prototype would have been something like
> 
>  int lookup(int key) const;
> 
> This was C++, so the const was at the end. The thing is, all the data was in a file, and so the function had to open a file, read it, close the file, and return the data. Well, naturally, it cached some data internally to avoid too many file accesses. And - also naturally - I did not load the entire file into memory at constructor time because huge parts of it might not be needed at all. The file access was deferred until it was needed, and cached appropriately.
> 
> 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.

My classic example for the need of "mutable" is a mutex used to synchronize access to class data.  The mutex must be modified even for 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.


Sean
« First   ‹ Prev
1 2 3 4 5 6 7 8