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