July 11, 2012
On 7/10/12 9:14 PM, Walter Bright wrote:
> Anyhow, the point of @trusted is to notify the maintainer that "here be
> dragons".

I think that's not representing @trusted quite accurately. There's no dragon there. @trusted means "the code is correct but not mechanically checkable". Casting away const is NOT correct.

Andrei


July 11, 2012
On 7/10/12 9:45 PM, Timon Gehr wrote:
> I do not desire logical const as a language feature. But conservative
> type systems are not good for everything. The root of the class
> hierarchy needs to be good for everything. Object is not an adequate
> root any more.

How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target?

Andrei
July 11, 2012
On 7/10/2012 6:53 PM, Andrei Alexandrescu wrote:
> On 7/10/12 9:14 PM, Walter Bright wrote:
>> Anyhow, the point of @trusted is to notify the maintainer that "here be
>> dragons".
>
> I think that's not representing @trusted quite accurately. There's no dragon
> there. @trusted means "the code is correct but not mechanically checkable".
> Casting away const is NOT correct.

You're right in that it would break immutable args passed.

PIMPL is a better option.


July 11, 2012
On 7/10/2012 6:07 PM, Timon Gehr wrote:
> (conceptually, the structures are immutable and infinite.)


What a minute - how are you doing toHash for an infinite structure?

The only way you could is by doing toHash on the initial condition for it. And that could be made const.

Same for opEquals.
July 11, 2012
On Wednesday, July 11, 2012 03:30:43 Jakob Ovrum wrote:
> Honestly, I think the ideal would be to give people the alternative of having a mutable opEquals etc. These methods only need to be logically constant (of course, they don't *need* to be even that, to the joy of operator overloading abusers worldwide), which means no help from the compiler, as it should be - just plain mutable, with the programmer providing the guarantee.
> 
> There was work in this direction but I understand it was ripe with issues of its own, however I don't understand how any other path could even be considered when it's just moving from one end of the scale to the other.
> 
> Yes, Object *needs* to work with const, this is imperative. But does it need to compromise on implementations relying on mutability? I was hoping the answer was "no". Obviously such an override would not work with const references, but some classes really don't lean themselves to immutability at all, alleviating the need for const. I can also imagine that if both were allowed, someone would leverage the ability to use caching in the mutable overload, then falling back to eager computation in the const overload.

Yeah. It seems to me that a reasonable approach would be to give Object two versions of opEquals, opCmp, toString, and toHash - a const and non-const version of each. The non-const version then calls the const version. So, the normal thing to do is have your class work with const and override the const version. If the non-const version on Object gets called, then the const version in the derived class gets called, and if the derived type is used directly, then it'll always use the const version, because that's what the derived type has.

On the other hand, for a class which doesn't work with const, it does this:

1. Override the non-const versions of opEquals, opCmp, toString, and toHash, giving them the implementations which you want.

2. Override the const versions of opEquals, opCmp, toString, and toHash and make their bodies assert(0).

That way, those 4 functions can be used normally as long as the object isn't assigned to a const reference, and if it is, then you get an Error. So, as long as the programmer completely avoids const with that class, it works just fine. And as long as the druntime functions which use those 4 functions do not require const (which is easy to do with either templates or inout), it should be perfectly possible to use such a class without it ever being const or ever needing to be const.

In addition, these may be possible (untested, so I'm not 100%) sure to make it harder to construct a const or immutable instance of the derived class:

1. @disable const and immutable versions of the class' constructors.

2. Create an alias this to an @disabled function which does an implicit conversion to const (probably won't work, but we could also probably make it possible if it doesn't currently work).

3. Create opCasts to const and immutable which are @disabled.

If all of those work, then the only way to get a const reference to the derived class is if it's used with a base class reference (which would probably only be Object given what the derived class is doing).

So, it then takes a bit of work to be able to have and use an object which cannot be const, but it's very doable, and a concise set of instructions on dlang.org would make it easy and straightforward to know what to do to create a class which can't be const. Maybe I'm missing something, but I don't see any reason why this can't work.

In addition, I see no reason to require that opEquals, opCmp, toString, or toHash be const, pure, @safe, _or_ nothrow for structs. As long as the druntime functions which use those functions are appropriately templated (which is pretty much required for them to work with structs, since they don't all derive from a single type), the constness, pureness, @safeness, and nothrowness of those functions in druntime can depend on whether the struct being used with them has those functions as const, pure, @safe, and/or nothrow.

So, with that, we can have const work wonderfully without requiring it, even if it does take a bit of work to get around it with classes. So, what am I missing here? Why doesn't this work? Or has Walter just not properly considered this option?

- Jonathan M Davis
July 11, 2012
On 07/11/2012 04:30 AM, Walter Bright wrote:
> On 7/10/2012 6:07 PM, Timon Gehr wrote:
>> (conceptually, the structures are immutable and infinite.)
>
>
> What a minute - how are you doing toHash for an infinite structure?
>
> The only way you could is by doing toHash on the initial condition for
> it. And that could be made const.
>
> Same for opEquals.

I am not. The structure is used to obtain unique representations for
values. Other parts of the code query the structure for the
representations of values. That query cannot be const because the
structure has to remember the representation it gives out if it has
never been requested before. Values can be compared for equality by
simple reference comparison that way, which speeds up the code
tremendously.
July 11, 2012
On 07/11/2012 04:02 AM, Andrei Alexandrescu wrote:
> On 7/10/12 9:45 PM, Timon Gehr wrote:
>> I do not desire logical const as a language feature. But conservative
>> type systems are not good for everything. The root of the class
>> hierarchy needs to be good for everything. Object is not an adequate
>> root any more.
>
> How about we consider just stiffening that upper lip and implement
> comparison and hashing without modifying their target?
>
> Andrei

It is promising that they never will in any derived class with no
obvious way out for no benefit that I struggle with.
July 11, 2012
On 7/10/2012 7:42 PM, Timon Gehr wrote:
> On 07/11/2012 04:30 AM, Walter Bright wrote:
>> On 7/10/2012 6:07 PM, Timon Gehr wrote:
>>> (conceptually, the structures are immutable and infinite.)
>>
>>
>> What a minute - how are you doing toHash for an infinite structure?
>>
>> The only way you could is by doing toHash on the initial condition for
>> it. And that could be made const.
>>
>> Same for opEquals.
>
> I am not. The structure is used to obtain unique representations for
> values. Other parts of the code query the structure for the
> representations of values. That query cannot be const because the
> structure has to remember the representation it gives out if it has
> never been requested before. Values can be compared for equality by
> simple reference comparison that way, which speeds up the code
> tremendously.

How about create a new instance of a struct when a new representation is requested?

July 11, 2012
On Wednesday, 11 July 2012 at 02:33:53 UTC, Jonathan M Davis wrote:
> -snip-
>
> So, with that, we can have const work wonderfully without requiring it, even
> if it does take a bit of work to get around it with classes. So, what am I
> missing here? Why doesn't this work? Or has Walter just not properly
> considered this option?
>
> - Jonathan M Davis

This is exactly the kind of balance I am hoping we can implement. I think Hara Kenji suggested something very similar at one point, but it had some kind of problem with it. It would be awesome if Kenji could provide some insight on that. I think his proposal is somewhere on Github, I'll have a look.
July 11, 2012
On Wednesday, 11 July 2012 at 02:02:52 UTC, Andrei Alexandrescu wrote:
> On 7/10/12 9:45 PM, Timon Gehr wrote:
>> I do not desire logical const as a language feature. But conservative
>> type systems are not good for everything. The root of the class
>> hierarchy needs to be good for everything. Object is not an adequate
>> root any more.
>
> How about we consider just stiffening that upper lip and implement comparison and hashing without modifying their target?
>
> Andrei

It's more likely to go down like this: programmer attempts to write his opEquals (or toString etc) within the restrictions of const, but fails due to the requirements of the implementation (which can easily go beyond simple performance measures like caching, as demonstrated). The programmer then writes his own mutable member function and neglects opEquals altogether. If the programmer is real nice, he/she will write a throwing opEquals stub.